Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_SRC_3_19910112 - unix-stuff/ccmdmd.unx
There is 1 other file named ccmdmd.unx in the archive. Click here to see a list.
/*
 Author: Andrew Lowry

 Columbia University Center for Computing Activities, July 1986.
 Copyright (C) 1986, 1987, 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.
*/

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

#include <sys/types.h>			/* needed by file.h on V7 */
#if SYSV
#include <sys/termio.h>
#include <fcntl.h>
#else
#include <sgtty.h>
#include <sys/file.h>			/* for open flags */
#endif
#include <stdio.h>			/* for FILE */
#include "ccmd.h"			/* get ccmd symbols */
#include "cmfncs.h"			/* and internal symbols */

/* 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 */

cmrpjmp()
{
  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 */

cmerjmp(ret)
int ret;				/* code that triggered the handler */
{
  cmperr(ret);				/* issue error message */
  longjmp(cmerjb,1);			/* take the jump */
  return(CMxNOAE);			/* failed */
}

cmerjnp(ret)
int ret;				/* code that triggered the handler */
{
  longjmp(cmerjb,1);			/* take the jump */
  return(CMxNOAE);			/* failed */
}

/*
** 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 char *nl = "\n";
static char *cr = "\r";
static char *cl,*ce,*up;		/* pointers to decoded entries */
int outc();				/* output routine for tputs */

char *tgetstr();			/* termlib routine returns string */

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

int cmgetc(c,fd)
char *c;				/* pointer where char is placed */
FILE * fd;				/* input file descriptor */
{
  int cc;				/* value from read */

  if (cmcsb._cmoj != NULL)
    cmflsh(cmcsb._cmoj);		/* flush pending output */
  if (fd == NULL)			/* no file descriptor.  EOF */
    return(CMxEOF);
  *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 */

cmputc(c,fd)
char c;					/* char to output */
FILE * fd;				/* output filedesc */
{

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

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

cmputs(s,fd)
char *s;				/* output string */
FILE *fd;				/* output filedesc */
{
  while(*s != '\0')
    cmputc(*s++,fd);
}

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

cmcr(fd)
FILE * fd;				/* output filedesc */
{
  cmputs(cr,fd);			/* use term specific sequence */
}

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

cmnl(fd)
FILE *fd;				/* output filedesc */
{
  cmputs(nl,fd);			/* use term-specific sequence */
}

/* cmflsh - flush output on fd */
cmflsh(fd)
FILE *fd;
{
  if (fd != NULL)
    fflush(fd);
}

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

cmwrap(fd)
FILE * fd;				/* output filedesc */
{
  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()
{
  if (cl == NULL)
    return(FALSE);			/* no clear screen sequence */
  else {
    tputs(cl,li,outc);			/* output the clear sequence */
    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.
**/

cmceol()
{
  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.
*/

cmupl()
{
  if (up == NULL)
    return(FALSE);			/* no upline sequence */
  else {
    tputs(up,1,outc);			/* else do the operation */
    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()
{
  return(cmcsb._cmcol);
}

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

cmflush(fd)
FILE *fd;
{
  if (fd != NULL) {
    if (isatty(fileno(fd)))		/* if it's a terminal */
#if SYSV
#define TIOCFLUSH TCFLSH
#endif
      ioctl(fileno(fd),TIOCFLUSH,NULL);	/* flush input and output */
    fd->_cnt = 0;			/* flush stdio IOBUF. */
  }
}

/* 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.
**/

cmtset()
{
  int ofd, ifd;
  char *areap = tcarea;			/* pointer to termcap decoding area */
  char *ttype;				/* terminal type */
  char *gttype();
 
  if (cmcsb._cmoj != NULL)
    ofd = fileno(cmcsb._cmoj);		/* input file descriptor */
  if (cmcsb._cmij != NULL)
    ifd = fileno(cmcsb._cmij);		/* output file designator */

  if (cmcsb._cmoj != NULL && isatty(ofd)) { /* check if it is a terminal */
    cmcsb._cmflg |= CM_TTY;		/* yup */
    ttype = gttype(ofd);		/* get the terminal type name */
    if (tgetent(tcapent,ttype) != 1) {	/* get termcap entry */
      cmcsb._cmflg &= ~CM_CRT;		/* no luck... assume hardcopy */
      cmcsb._cmcmx = 79;		/* use default max column */
      nl = "\n";			/* and set default newline */
      cr = "\r";			/* and return sequences */
    }
    else if (cmcsb._cmoj != NULL) {
      if (tgetflag("hc")) 		/* hardcopy indicated? */
        cmcsb._cmflg &= ~CM_CRT;	/* yup, note it */
      else {
        cmcsb._cmflg |= CM_CRT;		/* else flag a 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 */
      }
      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 = tgetnum("li");		/* get number of lines */
      if (li == -1)			/* no such entry? */
        li = 24;			/* use default */
      cmcsb._cmcmx = tgetnum("co");	/* get col count */
      if (cmcsb._cmcmx == -1)
        cmcsb._cmcmx = 79;		/* use default if not given */
      else
        cmcsb._cmcmx--;			/* else drop to max col position */
    }
    if (cmcsb._cmij != NULL && isatty(ifd)) {
      cmcsb._cmflg |= CM_TTY;		/* 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 | CM_CRT); /* not a tty, so not a crt either */
      cmcsb._cmcmx = 79;		/* and just use default width */
    }
  }
}

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

cmtend()
{
  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 */
    }
  }
}

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

static char *
gttype(fd)
int fd;
{
  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 */
  char typelin[80];			/* line from ttytype database */
  char *typecp;				/* pointer into type db entry */
  extern char *ttyname(), *getenv();

  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 */

#ifdef BSD
static struct sgttyb ttyblk, ttysav;	/* tty parameter blocks */
static struct ltchars ltc,ltcsav;	/* local special chars for new  */
#endif

#if SYSV
static struct termio ttyblk, ttysav;
#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
raw(fd)
int fd;
{
#if SYSV 
  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 . */
#endif /* SYSV */

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

  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 | CRMOD;	/* 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
}

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

static
unraw(fd)
int fd;
{
#if SYSV
  ioctl(fd,TCSETAW, &ttysav);		/* put back saved params */
#endif
#ifdef BSD
  ioctl(fd,TIOCSETP,&ttysav);		/* put back saved params */
  ioctl(fd,TIOCSLTC,&ltcsav);		/* restore local special chars */
#endif
}

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

PASSEDSTATIC
outc(c)
char c;
{
  FILE *fd = cmcsb._cmoj;		/* get output filedesc */

  if (fd != NULL)
    putc(c,fd);				/* do the write */
}

/* 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.
**/

#include <signal.h>			/* get signal symbols */
#ifdef V7				/* used improve calls in version 7 */
#define signal sigsys
#endif
#define mask(sig)   (1 << ((sig)-1))

static
intson()
{
  int sighand();			/* forward decl of our handler */
  int oldmask;				/* signal mask before we play */
  int (*oldhand)();			/* old handler */

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


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

static
intsoff()
{
  int sighand();			/* forward decl of our handler */
  int oldmask;				/* sig mask prior to our diddling */
  int (*oldhand)();			/* prior handler for a signal */

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


/* 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).
**/

static
sighand(sig,code,scp)
int sig,code;				/* sig is all we care about */
struct sigcontext *scp;
{
  int oldmask;				/* prior interrupt mask */
  long getpid();			/* pids are long */

  cmtend();				/* fix the terminal */
#ifdef BSD
  oldmask = sigsetmask(0);		/* let our signal get through */
#endif
  cmnl(stdout);				/* move to new line for looks */
  kill(getpid(),sig);			/* get the default action */
  cmtset();				/* redo the terminal if continued */
#ifdef BSD
  sigsetmask(oldmask);			/* set mask back to before */
#endif
}

/* 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()
{
  if (ttyfd == -1)
    ttyfd = open("/dev/tty",O_RDWR,0);
  return(ttyfd);
}

static
cttycls()
{
  if (ttyfd != -1)
    close(ttyfd);
  ttyfd = -1;
}