#module ckvtio "2.0-033"
char *ckxv = "TTY I/O, 2.0(034), 14 Jan 92";
char *ckxsys = " VAX/VMS";

/*  C K V T I O  --  Terminal and Interrupt Functions for VAX/VMS  */

/* C-Kermit interrupt, terminal control & i/o functions for VMS systems */

/*
  Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
  York.  Permission is granted to any individual or institution to use this
  software as long as it is not sold for profit.  This copyright notice must be
  retained.  This software may not be included in commercial products without
  written permission of Columbia University.
*/

/*  S. Rubenstein, Harvard University Chemical Labs  */
/*  (c) 1985 President and Fellows of Harvard College  */

/* Edit History
 * 006  8-May-85 MM   Got rid of "typeahead buffer" code as it didn't
 *		      solve the problem of data overruns at 4800 Baud.
 *		      Added vms "read a char" routine that checks for
 *		      CTRL/C, CTRL/Z, etc.
 * 007 16-May-85 FdC  Changed calling convention of ttopen(), make it
 *  	    	      set value of its argument "lcl", to tell whether
 *                    C-Kermit is in local or remote mode.
 * 008 11 Jun 85 MM   Fix definition of CTTNAM
 * 009 18 Jun 85 FdC  Move def of CTTNAM to ckcdeb.h so it can be shared.
 * 010 25 Jun 85 MM   Added sysinit() to open console.
 * 011  5 Jul 85 DS   Treat hangup of closed line as success.
 * 012 11 Jul 85 FdC  Add gtimer(), rtimer() for timing statistics.
 * 013 14 Sep 87 FdC  Add parity strip to ttinl(), add syscleanup().
 * 014 14 Feb 89 mab  Make break REALLY work.  Add IOSB's to all QIO's.
 * 015 26 Feb 89 mab  Add dcl exit handler and minor cleanup
 * 016 23-Mar-89 mab  Add IO$M_BREAKTHRU to IO$_WRITEVBLK.
 *		      Add zchkspd() function to check for valid speed.
 * 017 04-Apr-89 mab  Fix some minor bugs to lcl/remote code
 * 018 23-Apr-89 mab  Add some of Valerie Mates parity changes.
 * 019 25-Apr-89 mab  Change baud to 110 for V4.x break routine as
 * 		      50 baud is nto supported on most MUX's by VMS.
 * 020 13-Jun-89 mab  Fix on exquota problem on qiow(readvblk)
 * 021 08-Jul-89 mab  Add ^C/^Y abort server mode code.
 * 022 11-May-90 mab  Add V5A code 
 * 023 20-Jul-90 wb   Add support for old VAX C and VMS versions
 * 024 22-Sep-90 wb   Fixes to cps/bps confusion
 * 025 26-Sep-90 tmk  Fix the ztime() function
 * 026 29-Sep-90 tmk  Edit 024 cause a server command to give some blither-
 *		      ings about unsupport line speeds. Added a simple hack
 *		      to exit quietly if the passed speed is 0. Adventurous
 *		      maintainers may want to look in ttpkt(), where ttsspd()
 *		      is being called with a value of -1/10.
 * 027 11-Oct-90 fdc  Made ttol return number of chars successfully written.
 *                    Made ztime() use ctime() in all cases.
 *                    Another fix (from tmk) for bps/cps confusion.
 *                    Wrapped source lines longer than 80 characters.
 * 028 18-Oct-90 fdc  Added comments to concb() and vms_getchar() to show
 *                    how to make Ctrl-C trap work.  Didn't actually do it,
 *                    though, because Ctrl-Y apparently still can't be caught.
 *                    Also, more minor reformatting.  Adjust time() declare.
 *                    Added support for automatic parity sense in ttinl(),
 *                    within #ifdef PARSENSE conditionals.  Built with PARSENSE
 *                    defined, works ok.
 * 029  5-Apr-91 fdc  Extensive reworking of many routines to allow for
 *		      network connections.
 * 030 31-Aug-91 tmk  Fix problem with INPUT statements not timing out due to
 *		      extraneous rtimer() inside gtimer().
 * 032  6-Nov-91 fdc  Correct parity problem.
 * 032  6-Nov-91 fdc  Cosmetic cleanup in sysinit().
 * 033 14-Jan-91 fdc  Fix to get_qio_maxbuf_size to prevent crashes:
 *                    remove "&" from "!test_qio(ttychn,max,&tmpbuf)",
 *                    from John Schultz at 3M.
 * 034  8-Feb-92 fdc  Don't change EIGHTBIT setting in ttvt, concb, or conbin.
 *                    Set EIGHTBIT in ttpkt only if parity is not NONE.  From
 *                    Alan Robiette, Oxford U, UK.
 */

#ifdef VMS_V40
#define	OLD_VMS
#endif
#ifdef VMS_V42
#define	OLD_VMS
#endif
#ifdef VMS_V44
#define	OLD_VMS
#endif
#ifdef VAXC023
#define	OLD_VMS
#endif
#ifdef VAXC024
#define	OLD_VMS
#endif

/*
 Variables available to outside world:

   dftty  -- Pointer to default tty name string, like "/dev/tty".
   dfloc  -- 0 if dftty is console(remote), 1 if external line(local).
   dfprty -- Default parity
   dfflow -- Default flow control
   ckxech -- Flag for who echoes console typein:
     1 - The program (system echo is turned off)
     0 - The system (or front end, or terminal).
   functions that want to do their own echoing should check this flag
   before doing so.

   backgrd -- Flag indicating program executing in background ( & on 
		end of shell command). Used to ignore INT and QUIT signals.
   vms_status -- status returned by most recent system service
		which may be used for error reporting.

 Functions for assigned communication line (either external or console tty):

   ttopen(ttname,local,mdmtyp) -- Open the named tty for exclusive access.
   ttclos()                -- Close & reset the tty, releasing any access lock.
   ttpkt(speed,flow)       -- Put the tty in packet mode and set the speed.
   ttvt(speed,flow)        -- Put the tty in virtual terminal mode.
				or in DIALING or CONNECTED modem control state.
   ttinl(dest,max,timo)    -- Timed read line from the tty.
   ttinc(timo)             -- Timed read character from tty.
   ttchk()                 -- See how many characters in tty input buffer.
   ttxin(n,buf)            -- Read n characters from tty (untimed).
   ttol(string,length)     -- Write a string to the tty.
   ttoc(c)                 -- Write a character to the tty.
   ttflui()                -- Flush tty input buffer.
   tt_cancel()		   -- Cancel any asynch I/O to tty
*/

/*
Functions for console terminal:
   congm()   -- Get console terminal modes.
   concb(esc) -- Put the console in single-character wakeup mode with no echo.
   conbin(esc) -- Put the console in binary (raw) mode.
   conres()  -- Restore the console to mode obtained by congm().
   conoc(c)  -- Unbuffered output, one character to console.
   conol(s)  -- Unbuffered output, null-terminated string to the console.
   conola(s) -- Unbuffered output, array of lines to the console, CRLFs added.
   conxo(n,s) -- Unbuffered output, n characters to the console.
   conchk()  -- Check if characters available at console (bsd 4.2).
		Check if escape char (^\) typed at console (System III/V).
   coninc(timo)  -- Timed get a character from the console.
   conint()  -- Enable terminal interrupts on the console if not background.
   connoi()  -- Disable terminal interrupts on the console if not background.
   contti()  -- Get a character from either console or tty, whichever is first.

Time functions

   msleep(m) -- Millisecond sleep
   ztime(&s) -- Return pointer to date/time string
   rtimer()  -- Reset elapsed time counter
   gtimer()  -- Get elapsed time
*/

/* Includes */
#include "ckcdeb.h"			/* Formats for debug() */
#include "ckcasc.h"
#include "ckcker.h"

#include "ckvvms.h"
#include <stdio.h>			/* Unix Standard i/o */
#include <signal.h>			/* Interrupts */
#include <setjmp.h>			/* Longjumps */
#include <iodef.h>
#include <ttdef.h>
#include <tt2def.h>
#include <ssdef.h>
#include <descrip.h>
#include <dvidef.h>
#include <dcdef.h>
#include <devdef.h>
/* #ifndef OLD_VMS */
#include <time.h>
/* #endif */
#ifndef VMS_V40
#include <syidef.h>
#define	void	int
#endif
#ifndef OLD_VMS
#include <starlet.h>
#endif

/* Declarations */

#ifdef OLD_VMS
    long time();			/* Current time in secs */
#else
    time_t time();
#endif
    char *ctime();			/* Convert to asctime() string */

    void dcl_exit_h();			/* Exit handler */
    unsigned long int vms_assign_channel();

/* dftty is the device name of the default device for file transfer */
/* dfloc is 1 if dftty is the user's console terminal, 0 if an external line */

    char *dftty = CTTNAM;
    int dfloc = 0;			/* Default location is local */
    int dfprty = 0;			/* Parity (0 = none) */
    int ttprty = 0;			/* Parity in use */
    static int ttpmsk = 0377;
    int ttmdm = 0;                      /* Modem in use. */
    int dfflow = 1;			/* Xon/Xoff flow control */
    int batch = 0;			/* Assume interactive */
    int ttcarr = CAR_AUT;		/* Carrier Handling Mode */
    int tvtflg = 0;			/* Flag that ttvt has been called */
    long ttspeed = -1;			/* For saving speed */
    int ttflow = -9;			/* For saving flow */

int ckxech = 0; /* 0 if system normally echoes console characters, else 1 */

unsigned int vms_status;	    /* Used for system service return status */

/* Structures used within this module */

#ifndef TT$C_BAUD_38400
#define TT$C_BAUD_38400	0x11
#endif

static struct {
    unsigned char dec;
    unsigned short int line;
    } ttspeeds[] = {
	{TT$C_BAUD_50,       5},
	{TT$C_BAUD_75,       7},
	{TT$C_BAUD_110,     11},
	{TT$C_BAUD_134,     13},
        {TT$C_BAUD_150,     15},
	{TT$C_BAUD_300,     30},
	{TT$C_BAUD_600,     60},
	{TT$C_BAUD_1200,   120}, 
	{TT$C_BAUD_1800,   180}, 
	{TT$C_BAUD_2000,   200},
	{TT$C_BAUD_2400,   240},
	{TT$C_BAUD_3600,   360}, 
	{TT$C_BAUD_4800,   480},
	{TT$C_BAUD_7200,   720},
	{TT$C_BAUD_9600,   960}, 
	{TT$C_BAUD_19200, 1920},
	{TT$C_BAUD_38400, 3840},
	{0,                   0} };

/* Declarations of variables global within this module */

static long tcount;			/* For timing statistics */

static char *brnuls = "\0\0\0\0\0\0\0"; /* A string of nulls (for BREAK) */

int ttyfd = -1;				/* TTY file descriptor */
 
static int conif = 0,			/* Console interrupts on/off flag */
    conclass = 0,			/* Console device type */
    cgmf = 0,				/* Flag that console modes saved */
    xlocal = 0,                         /* Flag for tty local or remote */
    ttychn = 0,				/* TTY i/o channe; */
    conchn = 0,				/* Console i/o channel */
    con_queued = 0,			/* console i/o queued in contti() */
    tt_queued = 0,			/* tty i/o queued in contti() */
    conch,				/* console input character buffer  */
    curcarr = 0,			/* Carrier mode: require/ignore */
    ttch;				/* tty input character buffer */
static unsigned char escchr;		/* Escape or attn character */
static char ttnmsv[65];          	/* copy of open path for tthang */
static char lclnam[65];			/* Local device name */

static long int qio_maxbuf_size;	/* Maximum size of QIO to succeed */
static unsigned long dclexh_status;	/* Exit status for DCL exit handler */
static struct iosb_struct coniosb, ttiosb, wrk_iosb;
static struct tt_mode
    ttold, ttraw, tttvt,		/* for communication line */
    ccold, ccraw, cccbrk,		/* and for console */
    cctmp;

#ifdef COMMENT
/* These aren't used */
static int inbufc = 0;			/* stuff for efficient raw line */
static int ungotn = -1;			/* pushback to unread character */
#endif /* COMMENT */

/* Network support */

#include "ckcnet.h"			/* Network type symbols */
extern int ttnet;			/* Network type */
static int netconn = 0;			/* 1 if network connection */

SIGTYP (*saval)() = NULL;		/* For saving alarm handler */

VOID
ttimoff() {                           /* Turn off any timer interrupts */
    alarm(0);
    if (saval)
      signal(SIGALRM,saval);
    else
      signal(SIGALRM,SIG_DFL);
    saval = NULL;
}

/*  P R I N T _ M S G  --  Log an error message from VMS  */

print_msg(s) char *s; {
    long int blen = 0;
    char buf[PMSG_BUF_SIZE], msg[PMSG_MSG_SIZE];
    struct dsc$descriptor_s b = {
	PMSG_BUF_SIZE-1,
	DSC$K_DTYPE_T,
	DSC$K_CLASS_S,&buf
    };

    if (!((vms_status = sys$getmsg(vms_status, &blen, &b, 0, 0)) & 1)) {
	fprintf(stderr,"print_msg; sys$getmsg\n");
	return(-1);
    }
    buf[blen] = '\0';
    sprintf(msg, "%s: %s\n", s, buf);
    ermsg(msg);
}

/*  S Y S I N I T  --  System-dependent program initialization.  */

#ifndef DVI$_FULLDEVNAM
#define DVI$_FULLDEVNAM 232
#endif

#ifndef DVI$_STS
#define DVI$_STS 226
#endif

sysinit() {
    extern int speed;
    extern char ttname[];
    struct itmlst dviitm[] = {{64,DVI$_FULLDEVNAM,&lclnam,0},
				{sizeof(conclass),DVI$_DEVCLASS,&conclass,0},
				{0,0,0,0}};

    static struct desblk {
	long int *fl;			/* Forward link.  Used by VMS only */
	void (*fncpnt)();		/* Function to call */
	unsigned char argcnt;		/* Only one arg allowed */
	unsigned char filler[3];	/* Filler.  Must be zero */
	long int *sts;			/* Address of sts (written by VMS) */
    } dclexh_ = {0,dcl_exit_h,1,{0,0,0},&dclexh_status};

/*
 * Set up DCL Exit handler.  This allows us to reset terminal
 * and any other modifications we have done.
 */
    debug(F101,"sysinit ttychn","",ttychn);
    debug(F101,"sysinit conchn","",conchn);
    if (!CHECK_ERR("sysinit: sys$dclexh",
		   sys$dclexh(&dclexh_))) {
        debug(F100,"sysinit failed to declare exit handler","",0);
#ifdef COMMENT
	return(0);
#endif
    }
    if (ttychn)                     	/* if comms line already opened */
      return(0);			/* (how could it be???) */
    
    if (!conchn)			/* Get console channel */
      conchn = vms_assign_channel(dftty);
    congm();				/* Get and save its modes */
/*
 * Parse console terminal device name.
 */
    CHECK_ERR("sysinit: sys$getdviw",
	sys$getdviw(0, conchn, 0, &dviitm, &wrk_iosb, 0, 0, 0));
    debug(F111,"sysinit","lclnam",lclnam);

    if (!CHECK_ERR("sysinit: sys$qiow",
    	sys$qiow(QIOW_EFN, conchn, IO$_SENSEMODE, &wrk_iosb, 0, 0,
    	         &ccold, sizeof(ccold), 0, 0, 0, 0))) return(-1);

    speed = ttispd((unsigned char) wrk_iosb.size);
    debug(F111,"sysinit speed",lclnam,speed);
    strncpy(ttname,lclnam,DEVNAMLEN);		/* Max because of ckcmai */
    debug(F100,"sysinit done","",0);
    return(0);
}

/*
 * DCL Exit handler.  This is the cleanup handler for program.
 * Any final cleanup (closing channels etc) should be done at this
 * point.
 */
void
dcl_exit_h(sts) unsigned long int *sts; {
    syscleanup();
    return;
}

/*  S Y S C L E A NU P -- System-dependent program epilog.  */

syscleanup() {
    extern zclosf();

    ttclos();			/* Do the cleanup no matter what... */
    conres();			/* for the console also... */
    zclosf();			/* Close various files and kill child proc */
    printf("\r");
    return(0);
}

/*  T T O P E N  --  Open a tty for exclusive access.  */

/*  Returns 0 on success, -1 on failure.  */
/*
  If called with lcl < 0, sets value of lcl as follows:
  0: the terminal named by ttname is the job's controlling terminal.
  1: the terminal named by ttname is not the job's controlling terminal.
  But watch out: if a line is already open, or if requested line can't
  be opened, then lcl remains (and is returned as) -1.
*/
int
ttopen(ttname,lcl,modem) char *ttname; int *lcl, modem; {
    extern int speed;
    int s;
    unsigned long int devchar, devclass, devsts;
    char dvibuf[65];
    struct dsc$descriptor_s devnam = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
    struct itmlst dviitm[] = {{64,DVI$_FULLDEVNAM,&dvibuf,0},
			{sizeof(devchar),DVI$_DEVCHAR,&devchar,0},
			{sizeof(devclass),DVI$_DEVCLASS,&devclass,0},
			{sizeof(devsts),DVI$_STS,&devsts,0},
			{0,0,0,0}};

#ifdef NETCONN
    if (netconn && ttyfd > -1) {	/* if device already opened */
        if (strncmp(ttname,ttnmsv,DEVNAMLEN)) /* are new & old names equal? */
          ttclos(ttyfd);		/* no, close old ttname, open new */
        else 				/* else same, ignore this call, */
	  return(0);			/* and return. */
    }
    if (modem < 0) {			/* modem < 0 = special code for net */
	int x;
	ttmdm = modem;
	modem = -modem;			/* Positive network type number */
	debug(F111,"ttopen net",ttname,modem);
	x = netopen(ttname, lcl, modem); /* (see ckcnet.h) */
	if (x > -1) {
	    strncpy(ttnmsv,ttname,DEVNAMLEN);
	    netconn = 1;
	    x = tn_ini();		/* Initialize telnet protocol. */
	    ttnet = modem;
	} else netconn = 0;
        return(x);
    }
#endif /* NETCONN */

    devnam.dsc$w_length  = strlen(ttname);
    devnam.dsc$a_pointer = ttname;
    CHECK_ERR("ttopen: sys$getdviw",
	sys$getdviw(0, 0, &devnam, &dviitm, &wrk_iosb, 0, 0, 0));

    if ((devclass != DC$_TERM) ||
    	!(devchar & DEV$M_AVL) ||
	!(devsts & UCB$M_ONLINE)) {
	fprintf(stderr,
		"%%CKERMIT-W-NOTTERM, Device '%s' is not a terminal\n",dvibuf);
	return(-1);
    }
    dvibuf[65] = '\0';
    if (ttychn) return(0);		/* Close channel if open */

    ttmdm = modem;			/* Make this available to other fns */
    xlocal = *lcl;			/* Make this available to other fns */
/*
 * The following is 'kludgy', but it is the only way to make this work
 * without modifying CKUUS3.  I copy the returned device name into
 * the command line!  If another parameter were to follow this one (which
 * it does not at this time), it would be overwritten, possibly.
 */

    strncpy(ttname,dvibuf,50);

    ttychn = vms_assign_channel(ttname);

    debug(F111,"ttopen","modem",modem);
    debug(F101," ttychn","",ttychn);

    if (!ttychn) return(-1);		/* If couldn't open, fail. */

/*
 * Check for maximum size of QIO, so as to not get the dreaded exceeded quota
 * status returned.  When doing a QIO that has a larger buffer than
 * MAXBUF, exceeded quota wil be returned.  There is also another limit, lower
 * than MAXBUF, as to when extra features are used in the QIO, that more
 * space can be used.
 * 
 * Example: MAXBUF = 2048, QIO = 1936, overhead is 112 will succeed.
 *	    QIO of 1937 will fail.
 *
 * This can change for different versions of VMS.
 */

    qio_maxbuf_size = get_qio_maxbuf_size(ttychn);

    strcpy(ttnmsv,ttname);		/* Open, keep copy of name locally. */

/* Caller wants us to figure out if line is controlling tty */
 
    debug(F111,"ttopen ok",ttname,*lcl);
    if (*lcl < 0) {
	if (conclass == DC$_TERM)
	  xlocal = (strncmp(ttname,lclnam,DEVNAMLEN) == 0) ? 0 : 1;
	else
	  xlocal = 1;		     /* If not a term, then we must be local*/
/*
 * xlocal = 1 = local
 *          0 = remote
 */
	debug(F111,"ttyname",lclnam,xlocal);
    }

    if (!CHECK_ERR("ttopen: sys$qiow",
	sys$qiow(QIOW_EFN, ttychn, IO$_SENSEMODE, &wrk_iosb, 0, 0,
		 &ttold, sizeof(ttold), 0, 0, 0, 0))) return(-1);

    speed = ttispd((unsigned char) wrk_iosb.size);

/* Got the line, now set the desired value for local. */
 
    if (*lcl) *lcl = xlocal;

    tttvt = ttold;
    ttraw = ttold;
    debug(F101," lcl","",*lcl);
    return(0);
}

unsigned long int
vms_assign_channel(ttname) char *ttname;  {
    unsigned int channel = 0;
    struct dsc$descriptor_s d = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};

    d.dsc$w_length  = strlen(ttname);
    d.dsc$a_pointer = ttname;
    if (!CHECK_ERR("vms_assign_channel: sys$assign",
	sys$assign(&d, &channel, 0, 0))) return(0);
    return(channel);
}

/*  T T C L O S  --  Close the communication device.  */

int
ttclos() {
#ifdef	NETCONN
    if (netconn) {			/* Network connection. */
	debug(F100,"ttclos closing net","",0);
	netclos();			/* Close it. */
	netconn = 0;
	return(0);
    }
#endif	/* NETCONN */
    if (!ttychn) return(0);		/* Wasn't open. */
    ttres();				/* Reset modes. */
    if (!CHECK_ERR("ttclos: sys$dassgn",
	sys$dassgn(ttychn))) return(-1);
    ttychn = 0;				/* Mark it as closed. */
    return(0);
}


/*  T T R E S  --  Restore terminal to "normal" mode.  */

int
ttres() {				/* Restore the tty to normal. */
#ifdef	NETCONN
    if (netconn) return (0);		/* Network connection, do nothing */
#endif	/* NETCONN */

    if (!ttychn) return(-1);		/* Not open. */

    tt_cancel();			/* Cancel outstanding I/O */
    msleep(250);			/* Wait for pending i/o to finish. */
    debug(F101,"ttres, ttychn","",ttychn);
    if (!CHECK_ERR("ttres: sys$qiow",
	sys$qiow(QIOW_EFN, ttychn, IO$_SETMODE, &wrk_iosb, 0, 0,
    		 &ttold, sizeof(ttold), 0, 0, 0, 0))) return(-1);
    return(0);
}

/*  T T P K T  --  Condition the communication line for packets. */
/*		or for modem dialing */

#define DIALING	4		/* flags (via flow) for modem handling */
#define CONNECT 5

/*  If called with speed > -1, also set the speed.  */

/*  Returns 0 on success, -1 on failure.  */

int
ttpkt(speed,flow,parity) int speed, flow, parity; {
    extern char ttname[];
    int s;

    ttprty = parity;			/* Let other tt functions see these */
    ttpmsk = ttprty ? 0277 : 0377;	/* Parity stripping mask */
    debug(F101,"ttpkt ttprty","",ttprty);

#ifdef NETCONN
    if (netconn) return (0);
#endif /* NETCONN */

    if (!ttychn) return(-1);		/* Not open. */

    ttspeed = speed;
    ttflow = flow;

    if ((s = ttsspd(speed/10)) < 0) s = 0;
    if (flow == 1) ttraw.basic |=  (TT$M_HOSTSYNC|TT$M_TTSYNC);
    if (flow == 0) ttraw.basic &= ~(TT$M_HOSTSYNC|TT$M_TTSYNC);

#ifdef TT2$M_PASTHRU
    ttraw.extended |= TT2$M_PASTHRU;
#else
    ttraw.basic |= TT$M_PASSALL;
#endif

#ifdef TT2$M_ALTYPEAHD
    ttraw.extended |= TT2$M_ALTYPEAHD;
#else
/* NEED SOMETHING HERE to enable alt-typeahead buffer in older C versions! */
#endif

#ifdef COMMENT
/*
  No, set EIGHTBIT only if no parity.
*/
    ttraw.basic |= TT$M_NOECHO | TT$M_EIGHTBIT | TT$M_NOBRDCST;
#else
    if (parity)
      ttraw.basic |= TT$M_NOECHO  | TT$M_NOBRDCST;
    else
      ttraw.basic |= TT$M_NOECHO | TT$M_EIGHTBIT | TT$M_NOBRDCST;
#endif /* COMMENT */
    ttraw.basic &= ~TT$M_WRAP;
    if (!CHECK_ERR("ttpkt: sys$qiow",
	sys$qiow(QIOW_EFN, ttychn, IO$_SETMODE, &wrk_iosb, 0, 0,
    	    	 &ttraw, sizeof(ttraw), s, 0, 0, 0))) return(-1);
    ttflui();				/* Flush any pending input */
    return(0);
}

/*  T T V T -- Condition communication line for use as virtual terminal  */

int
ttvt(speed,flow) int speed, flow; {
    extern char ttname[];
    int s;

#ifdef NETCONN
    if (netconn) {
	tvtflg = 1;			/* Network connections */
	return(0);			/* require no special setup */
    }
#endif /* NETCONN */
    if (!ttychn) return(-1);		/* Not open. */

    if ((s = ttsspd(speed/10)) < 0) s = 0;

    if (flow == 1) ttraw.basic |=  (TT$M_HOSTSYNC|TT$M_TTSYNC);
    if (flow == 0) ttraw.basic &= ~(TT$M_HOSTSYNC|TT$M_TTSYNC);
#ifdef TT2$M_PASTHRU
    ttraw.extended |= TT2$M_PASTHRU;
#else
    ttraw.basic |= TT$M_PASSALL;
#endif
#ifdef TT2$M_ALTYPEAHD
    ttraw.extended |= TT2$M_ALTYPEAHD;
#endif
#ifdef COMMENT
/*
  No, leave EIGHTBIT setting alone.
*/
    ttraw.basic |= TT$M_NOECHO | TT$M_EIGHTBIT;
#else
    ttraw.basic |= TT$M_NOECHO;
#endif /* COMMENT */
    ttraw.basic &= ~TT$M_WRAP;
    if (!CHECK_ERR("ttvt: sys$qiow",
	sys$qiow(QIOW_EFN, ttychn, IO$_SETMODE, &wrk_iosb, 0, 0,
			  &ttraw, sizeof(ttraw), s, 0, 0, 0))) return(-1);
    return(0);
}

/* T T I S P D  -- Return binary baud rate for internal coded speed */

int
ttispd(ispeed) unsigned char ispeed; {
    int s;

#ifdef NETCONN
    if (netconn) return(-1);
#endif /* NETCONN */

/* When the line is set, grab the line speed  and save it */

    for (s = 0;  ttspeeds[s].dec && 
	(ttspeeds[s].dec != ispeed);  s++)
		;

/* If speed is zero, then no match.  Set speed to -1 so it is undefined */

    return(ttspeeds[s].line ? (int) ttspeeds[s].line * 10 : -1);

}


/*  T T S S P D  --  Return the internal baud rate code for 'speed'.  */

int
ttsspd(cps) int cps; {
    int s;
    char msg[50];

#ifdef	NETCONN
    if (netconn) return(0);
#endif	/* NETCONN */

    if (cps <= 0)			/* 026 Unknown cps fails	*/
	return (-1);
    for (s = 0;  ttspeeds[s].line && (ttspeeds[s].line != cps);  s++) ;
#ifdef COMMENT
/* It looks like the cps argument is bps, not cps like .line, so ... */
    if (!ttspeeds[s].line)
        for (s = 0;  ttspeeds[s].line && (ttspeeds[s].line*10 != cps);  s++) ;
#endif
    if (ttspeeds[s].line)
	return(ttspeeds[s].dec);
    else {
	sprintf(msg,"Unsupported line speed - %d\n",cps*10);
	ermsg(msg);
	ermsg("Current speed not changed\n");
	return(-1);
    }
}

/*  T T F L U I  --  Flush tty input buffer */

int
ttflui() {

    long n=0;

#ifdef NETCONN
    if (netconn) {
	return(netflui());
    } else
#endif /* NETCONN */

/* Not network... */

    if (!ttychn) return(-1);		/* No connection open. */

/* An open, non-network connection... */

    if (!CHECK_ERR("ttflui: sys$qiow",
	sys$qiow(QIOW_EFN, ttychn, IO$_READVBLK|IO$M_TIMED|IO$M_PURGE,
		 &wrk_iosb, 0, 0, &n, 0, 0, 0, 0, 0))) perror("flush failed");

#ifdef COMMENT
    /* Note: unused */
    inbufc = 0;
    ungotn = -1;
#endif /* COMMENT */
    return(0);
}

/* Interrupt Functions */


/*  C O N I N T  --  Console Interrupt setter  */

static int (*cctrap)();

VOID
conint(f,s) int (*f)(), (*s)(); {	/* Set an interrupt trap. */

    cctrap = f;				/* Make a global copy */
    debug(F101,"conint batch","",batch);
    if (batch) return;			/* Ignore signals in background. */

/* check if invoked in background -- if so signals set to be ignored */

#ifdef COMMENT
/* This is interfered with by connoi() */
    if (signal(SIGINT,SIG_IGN) == SIG_IGN) {
	debug(F100,"conint signal diagnoses batch","",0);
	batch = 1;			/* means running in background */
	return;
    }
#else
    if (!isatty(0)) {
	debug(F100,"conint isatty diagnoses batch","",0);
	batch = 1;			/*   means running in background */
	return;
    }
#endif
    signal(SIGINT,f);			/* Function to trap to. */
    conif = 1;				/* Flag console interrupts on. */
}


/*  C O N N O I  --  Reset console terminal interrupts */

VOID
connoi() {				/* Console-no-interrupts */

    if (batch) return;			/* must ignore signals in bkgrd */

#ifdef COMMENT
/* wrong... */
    signal(SIGINT,SIG_DFL);
#else
/* right?.. */
    signal(SIGINT,SIG_IGN);
#endif
    conif = 0;
}

/*  T T C H K  --  Tell how many characters are waiting in tty input buffer  */

int
ttchk() {
    static struct {
	unsigned short count; 
	unsigned char first; 
	unsigned char reserved1; 
	long reserved2; } ttchk_struct;

#ifdef NETCONN
    if (netconn) {			/* If network connection active... */
	return(nettchk());		/* Check real network. */
    }
#endif /* NETCONN */

    CHECK_ERR("ttchk: sys$qiow",
	sys$qiow(QIOW_EFN, ttychn, IO$_SENSEMODE|IO$M_TYPEAHDCNT, &wrk_iosb,
		 0, 0, &ttchk_struct, sizeof(ttchk_struct), 0, 0, 0, 0));
    debug(F101,"ttchk","",(int)ttchk_struct.count);
    return(vms_status & 1 ? ttchk_struct.count : 0);
}

/*  T T X I N  --  Get n bytes from tty input buffer  */
/*
  Call with n = number of bytes to get, buf = where to put them.

  This routine assumes that the given number of bytes is available
  and will not return until they are gotten.  You should only call this
  routine after calling ttchk to find out how many bytes are waiting to
  to be read.

  Returns:
  -1 on error, 0 if nothing happens, number of chars gotten on success.
*/
int
ttxin(n,buf) int n; char *buf; {
    int i, x;
    unsigned char *cp;
    static int trmmsk[2] = {0,0};

#ifdef NETCONN
    if (netconn) {
#ifdef MULTINET
	debug(F101,"ttxin multinet","",ttnet);
	i = 0;		
	while (i < n) {
	    if ((x = ttinc(0)) < 0) return(-1);
	    buf[i++] = (char) x;
	}
	buf[i] = '\0';
	return(i);
#endif /* MULTINET */
    } else {
#endif /* NETCONN */

	if (!CHECK_ERR("ttxin: sys$qiow",
		       sys$qiow(QIOW_EFN, ttychn, IO$_READVBLK, &wrk_iosb,
				0, 0, buf, n, 0, &trmmsk, 0, 0))
	    || !CHECK_ERR("ttxin: wrk_iosb.status",wrk_iosb.status))
	  return(-1);
	else n = wrk_iosb.size;

#ifdef NETCONN
    }
#endif /* NETCONN */

    if (ttprty)
	for (i = n, cp = buf; (i); i--)
	  *cp++ &= 0177;
    buf[n] = '\0';
    return(n);
}

/*  T T O L  --  Similar to "ttinl", but for writing.  */
/*
 * This probably should be buffered with a flush command added.
 */

#ifndef IO$M_BREAKTHRU
#define IO$M_BREAKTHRU	0x0200
#endif

int
ttol(s,n) int n; char *s; {
    int x;

    debug(F101,"ttol netconn","",netconn);
#ifdef NETCONN
    if (netconn) {
	x = nettol(s,n);		/* Call network package. */
    } else {
#endif /* NETCONN */
	debug(F101,"ttinc ttychn","",ttychn);
	if (!ttychn) return(-1);		/* Not open. */
	if (CHECK_ERR("ttol: sys$qiow",
		      sys$qiow(QIOW_EFN, ttychn, IO$_WRITEVBLK|IO$M_BREAKTHRU,
			       &wrk_iosb, 0, 0, s, n, 0, 0, 0, 0)))
	  x = n;			/* was x = 0 */
	else x = -1;
#ifdef NETCONN
    }
#endif /* NETCONN */
    debug(F111,"ttol",s,n);
    if (x < 0) debug(F101,"ttol failed","",x);
    return(x);
}

/*  T T O C  --  Output a character to the communication line  */

int
ttoc(c) char c; {
    debug(F101,"ttoc netconn","",netconn);
#ifdef NETCONN
    if (netconn) {
	return(nettoc(c));
    } else {
#endif /* NETCONN */
	debug(F101,"ttinc ttychn","",ttychn);
	if (!ttychn) return(-1);		/* Not open. */
	
	if (CHECK_ERR("ttoc: sys$qiow",
		      sys$qiow(QIOW_EFN, ttychn, IO$_WRITEVBLK|IO$M_BREAKTHRU,
			       &wrk_iosb, 0, 0, &c, 1, 0, 0, 0, 0)))
	  return(0);
#ifdef NETCONN
    }
#endif /* NETCONN */
    return(-1);
}

/*  T T I N L  --  Read a record (up to break character) from comm line.  */
/*
  If no break character encountered within "max", return "max" characters,
  with disposition of any remaining characters undefined.  Otherwise, return
  the characters that were read, including the break character, in "dest" and
  the number of characters read as the value of function, or 0 upon end of
  file, or -1 if an error occurred or, -2 if two ^C's were typed.  Times
  out & returns error if not completed within "timo" seconds.
*/
int
#ifdef PARSENSE
ttinl(dest,max,timo,eol,start) int max,timo; CHAR *dest, eol, start; {
#else
ttinl(dest,max,timo,eol) int max,timo; CHAR *dest, eol; {
#endif /* PARSENSE */
    int x, y, c, i, j;
    static int trmmsk[2] = {0,0};
    static int trmlong[8] = {0,0,0,0,0,0,0,0};
    int ccnt = 0;		/* Control C counter */
    int func;
    int flag;
    int ttpmsk;
    char cc;
    unsigned char *cp;

#ifdef NETCONN
    if (netconn) {
#ifdef MULTINET
	debug(F101,"ttinl multinet start","",start);
	i = j = flag = 0;
	ttpmsk = (ttprty) ? 0177 : 0377; /* Set parity stripping mask. */
	while (i < max) {
	    if ((cc = ttinc(0)) < 0) return(-1); /* Read a byte */
	    if ((flag == 0) && ((cc & 0x7f) == start)) {
		debug(F100,"ttinl got start","",0);
		flag = 1;		/* Got packet start. */
	    }
	    if (flag) {			/* If we're in a packet... */
		dest[i++] = cc & ttpmsk;
		if ((cc & 0x7f) == eol) { /* Stop at eol. */
		    debug(F101,"ttinl got eol, i","",i);
		    break;
		}
	    }
	}
	dest[i] = '\0';			/* Terminate the string */
	debug(F101,"ttinl loop done, i","",i);
	debug(F101,"ttinl max","",max);
	debug(F101,"ttinl dest[i-1]","",dest[i-1]);
	debug(F101,"ttinl eol","",eol);
	if (i >= max) {
	    debug(F100,"ttinl buffer overflow","",0);
	    return(-1);	/* Overflowed dest buffer without getting eol */
	}
	x = i;				/* Size. */
	debug(F111,"ttinl got packet",dest,x);

#endif /* MULTINET */
    } else {
#endif /* NETCONN */

	if (!ttychn) return(-1);	/* Not open. */

#ifdef PARSENSE
	debug(F000,"ttinl start","",start);
	trmmsk[0] = sizeof(trmlong);
	trmmsk[1] = &trmlong;
	trmlong[0] = (1 << eol) | CKV_M_CTRLC | CKV_M_CTRLY;
	trmlong[4] = (1 << eol) | CKV_M_CTRLC | CKV_M_CTRLY;
#else
	debug(F100,"ttinl no parsense","",0);
	if (ttprty) {
	    trmmsk[0] = sizeof(trmlong);
	    trmmsk[1] = &trmlong;
	    trmlong[0] = (1 << eol) | CKV_M_CTRLC | CKV_M_CTRLY;
	    trmlong[4] = (1 << eol) | CKV_M_CTRLC | CKV_M_CTRLY;
	} else {
	    trmmsk[0] = 0;
	    trmmsk[1] = (1 << eol) | CKV_M_CTRLC | CKV_M_CTRLY;
	}
#endif /* PARSENSE */
	func = IO$_READVBLK | IO$M_NOFILTR;
	if (timo > 0) func |= IO$M_TIMED;

/*
 * Limit size of QIO so we do not get exquota error.
 */
	if (max > qio_maxbuf_size) max = qio_maxbuf_size;

	do {
	    vms_status = sys$qiow(QIOW_EFN, ttychn, func, &wrk_iosb, 0, 0,
				  dest, max, timo, &trmmsk, 0, 0);

/*
 * Did any type of error occur on submission of QIO or execution of QIO?
 */
	    if ((!(vms_status & 1)) ||
		(!(wrk_iosb.status & 1))) {
/*
 * If we got a time out, then fine, return with a failure, but no messages
 */
		if (wrk_iosb.status == SS$_TIMEOUT)
		  return(-1);
/*
 * If the submission of QIO was fine, but the execution failed
 * then save the status so when the error is printed out, we know
 */
	
		if (!(vms_status & 1))
		  print_msg("ttinl: sys$qiow");
		if (!(wrk_iosb.status & 1)) {
		    vms_status = wrk_iosb.status;
		    print_msg("ttinl: sys$qiow(iosb)");
		}
		return(-1);
	    }
/*
 * Check for Control-C or -Y typed...
 */
	    if ((((CHAR) wrk_iosb.terminator & 0177) == CKV_K_CTRLC) ||
		(((CHAR) wrk_iosb.terminator & 0177) == CKV_K_CTRLY)) {
		if (++ccnt > 1) {
		    fprintf(stderr,"^C...");
		    ttres();
		    fprintf(stderr,"\n");
		    return(-2);
		}
	    } else ccnt = 0;
	} while (ccnt);
	x = wrk_iosb.size;
#ifdef NETCONN
    }
#endif /* NETCONN */

    debug(F101,"ttinl size","",x);
#ifdef PARSENSE
    for (i = 0; i < x; i++)		/* Find start of packet */
      if ((dest[i] & 0x7f) == start) break;
    if (i > 0) {
	for (j = i; j < x; j++)		/* Left-adjust packet in dest */
	  dest[j-i] = dest[j];
	x -= i;				/* Adjust the length */
    }
    dest[x] = '\0';			/* Terminate with null */
    debug(F101,"ttinl adjusted size by","",x);

    if (ttprty == 0) {			/* check and adjust the parity. */
        if ((ttprty = parchk(dest,start,x)) > 0)
	  debug(F000,"ttinl parchk senses parity","",ttprty);
	if (ttprty < 0) ttprty = 0;	/* restore if parchk error */
    }
#endif /* PARSENSE */

    if (ttprty) {			/* Now strip parity, if any. */
	cp = dest+i;
	for (i = x; i > 0; i--) {
	    *cp++ &= 0177;
	}
    }
    return(x);				/* Return length */
}

/*  T T I N C --  Read a character from the communication line  */

int
ttinc(timo) int timo; {
    unsigned char ch[2];
    static int trmmsk[2] = {0,0};
    int x, func;

#ifdef NETCONN
    if (netconn) {
#ifdef MULTINET
	if ((x = netinc(timo)) < 0) return(-1);
	ch[0] = x;
	ch[1] = '\0';
	debug(F111,"ttinc multinet",ch,ch[0]);
#endif /* MULTINET */
    } else {
	/* Fill in other networks here... */
#endif /* NETCONN */

	debug(F101,"ttinc ttychn","",ttychn);
	if (!ttychn) return(-1);	/* Not open. */
	func = IO$_READVBLK | IO$M_NOFILTR;
	if (timo > 0) func |= IO$M_TIMED;
	if (!CHECK_ERR("ttinc: sys$qiow",
		       sys$qiow(QIOW_EFN, ttychn, func, &wrk_iosb, 0, 0,
				&ch, 1, timo, &trmmsk, 0, 0))
	    || wrk_iosb.status == SS$_TIMEOUT /* Check separate so no errmsg */
	    || !CHECK_ERR("ttinc: wrk_iosb.status",wrk_iosb.status))
	  return(-1);
#ifdef NETCONN
    }
#endif /* NETCONN */
    return(ttprty ? ch[0] & 0177 : ch[0] & 0xff);
}

/*  T T _ C A N C E L  --  Cancel i/o on tty channel if not complete  */

VOID
tt_cancel() {
    int mask;
#ifdef NETCONN
    if (netconn) return;
#endif /* NETCONN */
    CHECK_ERR("tt_cancel: sys$cancel",sys$cancel(ttychn));
    tt_queued = 0;
}

/*  C O N _ C A N C E L  --  Cancel i/o on console channel if not complete  */

VOID
con_cancel() {
    int mask;

    CHECK_ERR("con_cancel: sys$cancel",sys$cancel(conchn));
    con_queued = 0;
}

/*  T T S N D B  --  Send a BREAK signal  */

int
ttsndb() {
    int long x = 0;
    struct iosb_struct  tmp_ttiosb;
    struct tt_mode ttchr;

#ifdef NETCONN
    if (netconn) 			/* Send network BREAK */
      return(netbreak());
#endif /* NETCONN */

    if (!ttychn) return(-1);		/* Not open. */

    tt_cancel();
#ifndef TT$M_BREAK
    if (!CHECK_ERR("ttsndb: SENSEMODE",
	sys$qiow(QIOW_EFN, ttychn, IO$_SENSEMODE, &wrk_iosb, 0, 0,
		&ttchr, sizeof(ttchr), 0, 0, 0, 0))) return(-1);
    if (!CHECK_ERR("ttsndb: SETMODE(1)",
	sys$qiow(QIOW_EFN, ttychn, IO$_SETMODE, &tmp_ttiosb, 0, 0,
		&ttchr, sizeof(ttchr), TT$C_BAUD_110, 0, 0, 0))) return(-1);
    if (!CHECK_ERR("ttsndb: writing nulls",
	sys$qiow(QIOW_EFN, ttychn, IO$_WRITEVBLK|IO$M_BREAKTHRU, &tmp_ttiosb,
		 0, 0, brnuls, 6, 0, 0, 0, 0))) return(-1);
    if (!CHECK_ERR("ttsndb: SETMODE(2)",
	sys$qiow(QIOW_EFN, ttychn, IO$_SETMODE, &tmp_ttiosb, 0, 0,
		&ttchr, sizeof(ttchr), wrk_iosb.size, 0, 0, 0))) return(-1);
#else
    if (!CHECK_ERR("ttsndb: SENSEMODE",
	sys$qiow(QIOW_EFN, ttychn, IO$_SENSEMODE, &wrk_iosb, 0, 0,
		&ttchr, sizeof(ttchr), 0, 0, 0, 0))) return(-1);
/* Break signal on */
    x = TT$M_BREAK;    
    if (!CHECK_ERR("ttsndb: SETMODE(1)",
	sys$qiow(QIOW_EFN, ttychn, IO$_SETMODE, &wrk_iosb, 0, 0,
		&ttchr, sizeof(ttchr), 0, 0, x, 0))) return(-1);
    msleep(275);
/* Break signal off */
    x = 0;
    if (!CHECK_ERR("ttsndb: SETMODE(2)",
	sys$qiow(QIOW_EFN, ttychn, IO$_SETMODE, &wrk_iosb, 0, 0,
		&ttchr, sizeof(ttchr), 0, 0, x, 0))) return(-1);
#endif /* TT$M_BREAK */
    return(0);
}

/*  T T H A N G  --  Hang up the communications line  */
/*
  Warning: As written, this function DOES NOT WORK on terminal server
  ports.  This is a shortcoming of VMS, confirmed by the Digital Diagnostic
  Center (or whatever DDC stands for).  Someone should add code here to test
  if the ttychn device is not a real terminal, and if so to handle it some
  other way, like set the speed to zero for a sec, or close and reopen the
  device.
*/
int
tthang() {

#ifdef NETCONN
    if (netconn) {			/* Network connection. */
	int x;
	if (netclos() < 0) return(-1);	/* Close it */
        tvtflg = 0;
        x = 1;
	netopen(ttnmsv, &x, ttmdm);	/* Open it again */
        return(1);
    }
#endif /* NETCONN */

    if (!ttychn) return(0);			/* Not open. */

    tt_cancel();
    if (!CHECK_ERR("tthang: sys$qiow",
	sys$qiow(QIOW_EFN, ttychn, IO$_SETMODE|IO$M_HANGUP, &wrk_iosb, 0, 0,
		0, 0, 0, 0, 0, 0))) return(-1);
    return(0);
}

/*  M S L E E P  --  Millisecond version of sleep().  */

/*
 Handles intervals up to about 7 minutes (2**32 / 10**7 seconds)
*/
msleep(m) int m; {

    struct time_struct {
	long int hi, lo;
    } t;
    if (m <= 0) return(0);
    t.hi = -10000 * m;  /*  Time in 100-nanosecond units  */
    t.lo = -1;
    if (!CHECK_ERR("msleep: sys$schdwk",
	sys$schdwk(0, 0, &t, 0))) return(-1);
    sys$hiber();
    return(0);
}

/*  R T I M E R --  Reset elapsed time counter  */

rtimer() {
    tcount = time( (long *) 0);
}


/*  G T I M E R --  Get current value of elapsed time counter in seconds  */

gtimer() {
    int x;
    x = (int) (time( (long *) 0 ) - tcount);
#ifdef COMMENT
/* This is a bug! */
    rtimer();
#endif /* COMMENT */
    return( (x < 0) ? 0 : x );
}

/*  Z T I M E  --  Return date/time string  */

ztime(s) char **s; {
    static long clock;
#ifdef COMMENT
#ifdef bogus
    static char time_string[24];
    struct dsc$descriptor_s t =
	{sizeof(time_string)-1,DSC$K_DTYPE_T,DSC$K_CLASS_S,&time_string};

    if (!CHECK_ERR("ztime: sys$asctim",
	sys$asctim(0, &t, 0, 0))) return(-1);
    time_string[t.dsc$w_length] = '\0';
    *s = &time_string;
#else
    char *asctime();
    struct tm *tp;

    time(&clock);
    tp = localtime(&clock);
    *s = asctime(tp);
#endif /* OLD_VMS */
#else /* not COMMENT */
/*
 Apparently ctime() is available in old C libraries, even though asctime()
 is not.  Let's use the same method for all versions.
*/
    time(&clock);
    *s = ctime(&clock);
#endif /* COMMENT */
}

/*  C O N G M  --  Get console terminal modes.  */

/*
 Saves current console mode, and establishes variables for switching between 
 current (presumably normal) mode and other modes.
*/

int
congm() {
    char s[] = "SYS$INPUT:";
    struct itmlst dviitm[] = { {4,DVI$_DEVCLASS,&dviitm[0].adr,0},
			{0,0,0,0}};
    struct dsc$descriptor_s
	r = {sizeof(s),DSC$K_DTYPE_T,DSC$K_CLASS_S,&s};

    debug(F101,"congm cgmf","",cgmf);
    if (cgmf) return(-1);		/* If called already, then nop */

    if (!CHECK_ERR("congm: sys$getdviw",
	sys$getdviw(0, 0, &r, &dviitm, &wrk_iosb, 0, 0, 0))) return(-1);
    debug(F101, "congm: devclass", "", (unsigned long int) dviitm[0].adr);
    if ((unsigned long int) dviitm[0].adr != DC$_TERM)
	batch = 1;
    else {
        debug(F101, "congm: conchn", "", conchn);
	if (conchn == 0 && (conchn = vms_assign_channel("SYS$INPUT:")) == 0)
	    return(-1);
        if (!CHECK_ERR("congm: sys$qiow",
    	sys$qiow(QIOW_EFN, conchn, IO$_SENSEMODE, &wrk_iosb, 0, 0,
    	         &ccold, sizeof(ccold), 0, 0, 0, 0))) return(-1);
        ccraw = cccbrk = ccold;
    }
    cgmf = 1;				/* Flag that we got them. */
    return(0);
}

/*  C O N C B --  Put console in cbreak mode.  */

/*  Returns 0 if ok, -1 if not  */

int
concb(esc) char esc; {
    int x;

    debug(F101,"concb batch","",batch);
    if (batch) return(0);
    if (!cgmf) congm();			/* Get modes if necessary. */
    escchr = esc;			/* Make this available to other fns */
    ckxech = 1;				/* Program can echo characters */
/*
  Note: PASTHRU / PASSALL is what is preventing the Ctrl-C trap in the
  main program from working.  This business can be removed without any effect
  at all on the command parser -- everything still works: completion, ?-help,
  editing, etc.  The only problem is that Ctrl-Y is not trapped, so the
  program dies and leaves the terminal in no-echo mode.
*/
#ifdef TT2$M_PASTHRU
    cccbrk.extended |= TT2$M_PASTHRU | TT2$M_DMA;
#else
    cccbrk.basic |= TT$M_PASSALL;
#endif

#ifdef TT2$M_ALTYPEAHD
    cccbrk.extended |= TT2$M_ALTYPEAHD;
#endif
#ifdef COMMENT
/*
  No, leave EIGHTBIT alone.
*/
    cccbrk.basic |= TT$M_NOECHO | TT$M_EIGHTBIT;
#else
    cccbrk.basic |= TT$M_NOECHO;
#endif /* COMMENT */
    cccbrk.basic &= ~(TT$M_WRAP);
/*    cccbrk.basic &= ~(TT$M_WRAP | TT$M_HOSTSYNC | TT$M_TTSYNC); */
    if (!CHECK_ERR("concb: sys$qiow",
	sys$qiow(QIOW_EFN, conchn, IO$_SETMODE, &wrk_iosb, 0, 0,
    	    	 &cccbrk, sizeof(cccbrk), 0, 0, 0, 0))) return(-1);
    debug(F100,"concb ok","",0);
    return(0);
}

/*  C O N B I N  --  Put console in binary mode  */

/*  Returns 0 if ok, -1 if not  */

int
conbin(esc) char esc; {

    debug(F101,"conbin batch","",batch);
    if (batch) return(0);
    if (!cgmf) congm();			/* Get modes if necessary. */
    escchr = esc;			/* Make this available to other fns */
    ckxech = 1;				/* Program can echo characters */
#ifdef TT2$M_PASTHRU
    ccraw.extended |= TT2$M_PASTHRU | TT2$M_DMA;
#else
    ccraw.basic |= TT$M_PASSALL;
#endif
#ifdef TT2$M_ALTYPEAHD
    ccraw.extended |= TT2$M_ALTYPEAHD;
#endif
#ifdef COMMENT
/*
  No, leave EIGHTBIT alone.
*/
    ccraw.basic |= TT$M_NOECHO | TT$M_EIGHTBIT;
#else
    ccraw.basic |= TT$M_NOECHO;
#endif /* COMMENT */
    ccraw.basic |= TT$M_NOECHO | TT$M_EIGHTBIT;
    ccraw.basic &= ~(TT$M_WRAP | TT$M_HOSTSYNC | TT$M_TTSYNC);
    if (!CHECK_ERR("conbin: sys$qiow",
	sys$qiow(QIOW_EFN, conchn, IO$_SETMODE, &wrk_iosb, 0, 0,
    	    	 &ccraw, sizeof(ccraw), 0, 0, 0, 0))) return(-1);
    return(0);
}


/*  C O N R E S  --  Restore the console terminal  */

int
conres() {
    debug(F101,"conres cgmf","",cgmf);
    if (!cgmf) return(0);		/* Do nothing if modes unknown */
    if (batch) return(0);

    msleep(250);			/*  not known! */
    ckxech = 0;				/* System should echo chars */
    if (!CHECK_ERR("conres: sys$qiow",
	sys$qiow(QIOW_EFN, conchn, IO$_SETMODE, &wrk_iosb, 0, 0,
	&ccold, sizeof(ccold), 0, 0, 0, 0))) return(-1);
    debug(F100,"conres ok","",0);
    return(0);
}


/*  C O N R E S N E --  Restore the console terminal with No Echo */

int
conresne() {
    debug(F101,"conresne cgmf","",cgmf);
    if (!cgmf) return(0);		/* Don't do anything if modes unk */
    if (batch) return(0);

    msleep(250);
    ckxech = 1;				/* Program should echo chars */

    cctmp = ccold;
    cctmp.basic |= TT$M_NOECHO;
    if (!CHECK_ERR("conres: sys$qiow",
	sys$qiow(QIOW_EFN, conchn, IO$_SETMODE, &wrk_iosb, 0, 0,
	&cctmp, sizeof(cctmp), 0, 0, 0, 0))) return(-1);
    debug(F100,"conresne ok","",0);
    return(0);
}

/*  C O N O C  --  Output a character to the console terminal  */

int
conoc(c) char c; {
    if (batch) putchar(c);
    else
	if (!CHECK_ERR("conoc: sys$qiow",
	    sys$qiow(QIOW_EFN, conchn, IO$_WRITEVBLK|IO$M_BREAKTHRU,
		     &wrk_iosb, 0, 0, &c, 1, 0, 0, 0, 0))) return(-1);
    return(1);
}

/*  C O N X O  --  Write x characters to the console terminal  */

int
conxo(x,s) char *s; int x; {
    if (batch) fprintf(stdout, "%.*s", x, s);
    else if (!CHECK_ERR("conxo: sys$qiow",
	sys$qiow(QIOW_EFN, conchn, IO$_WRITEVBLK|IO$M_BREAKTHRU,
		 &wrk_iosb, 0, 0, s, x, 0, 0, 0, 0))) return(-1);
    return(0);
}

/*  C O N O L  --  Write a line to the console terminal  */

int
conol(s) char *s; {
    int len;

    if (batch) fputs(s, stdout);
    else {
	len = strlen(s);
	if (!CHECK_ERR("conol: sys$qiow",
	    sys$qiow(QIOW_EFN, conchn, IO$_WRITEVBLK|IO$M_BREAKTHRU, &wrk_iosb,
		     0, 0, s, len, 0, 0, 0, 0))) return(-1);
    }
    return(1);
}

/*  C O N O L A  --  Write an array of lines to console, with CRLFs added */

int
conola(s) char *s[]; {
    int i;
    char t[100], *cp;

    for (i=0 ; *s[i] ; i++) {
	strncpy(t,s[i],100);
	for (cp = t + strlen(t); --cp >= t;) {
	    if (*cp != '\n' && *cp != '\r') {
		cp++;
		*cp++ = '\r'; *cp++ = '\n'; *cp++ = '\0';
		break;
	    }
	}
	conol(t);
    }
    return(0);
}

/*  C O N O L L  --  Output a string followed by CRLF  */

int
conoll(s) char *s; {
    conol(s);
    conol("\r\n");
    return(1);
}


/*  C O N C H K  --  Check if characters available at console  */

int
conchk() {
    struct {
	unsigned short count; 
	unsigned char first; 
	unsigned char reserved1; 
	long reserved2;
	} t;

    if (batch) return(0);
    return(CHECK_ERR("conchk: sys$qiow",
	sys$qiow(QIOW_EFN, conchn, IO$_SENSEMODE|IO$M_TYPEAHDCNT, &wrk_iosb,
		 0, 0, &t, sizeof(t), 0, 0, 0, 0)) ? t.count : 0);
}

/*  C O N I N C  --  Get a character from the console  */

int
coninc(timo) int timo; {
    int n = 0;
    unsigned char ch;
    int func, mask;

    if (batch) return(getchar());
    mask = 1 << CON_EFN;
    if (con_queued) {
	if (timo > 0) {
	    struct { int hi, lo; } qtime;
	    qtime.hi = -10*1000*1000*timo;  /* Max about seven minutes */
	    qtime.lo = -1;
	    sys$setimr(TIM_EFN, &qtime, 0, 0);
	    mask |= TIM_EFN;
	}
	sys$wflor(CON_EFN, mask);
	sys$readef(CON_EFN, &mask);
	if (mask & (1 << CON_EFN)) {
	    ch = (unsigned char) conch;
	    CHECK_ERR("coninc: coniosb.status", coniosb.status);
	    con_queued = 0;
	} else {
	    ch = -1;
	    vms_status = SS$_TIMEOUT;
	}
    } else {
	func = IO$_READVBLK | IO$M_NOFILTR;
	if (timo > 0) func |= IO$M_TIMED;
	CHECK_ERR("coninc: sys$qiow",
	    sys$qiow(QIOW_EFN, conchn, func, &wrk_iosb,0,0,&ch,1,timo,0,0,0));
    }
    if (ch == '\r') ch = '\n';
    if (vms_status & 1) return(ch);
    return(-1);
}


/*  V M S _ G E T C H A R -- get a character from the console (no echo).
 *	Since we use raw reads, we must check for ctrl/c, ctrl/y and
 *	ctrl/z ourselves.  We probably should post a "mailbox" for
 *	ctrl/c and ctrl/y so the poor user can abort a runaway Kermit.
 *	Note: this routine intends for ctrl/z (eof) to be "permanent".
 *	Currently, no kermit routine calls "clearerror".  If this
 *	changes, the following code must be rewritten.
 */

int
vms_getchar() {
    register unsigned int ch;
    static int ateof = FALSE;

    if (ateof)
      return (EOF);
    ch = coninc(0);
    switch (ch) {
      case ('Y' - 64):
      case ('C' - 64):
#ifndef COMMENT
/*
  Just call the same handler that signal(SIGINT,xxx) would have invoked
  if Ctrl-C had been trapped.  The pointer to the handler was saved in 
  cctrap by conint().
*/
	if (cctrap)
	  (*cctrap)(SIGINT,0);
#else
	ttclos();			/* Close down other terminal	*/
	conres();			/* And cleanup console modes	*/
	exit(SS$_ABORT);		/* Fatal exit.			*/
#endif
      case ('Z' - 64):
	ateof = TRUE;
	return (EOF);

      default:
	return (ch);
    }
}

/*  C O N T T I  --  Get character from console then from tty  */
/*
  This is used in conect() when NO_FORK is defined.
  src is returned with 1 if the character came from the comm. line,
  0 if it was from the console, and with -1 if there was any error.
*/

int
contti(c, src) int *c, *src; {
    int mask = 1<<CON_EFN | 1<<TTY_EFN;
    int x; unsigned char cc;

    *src = -1;				/* Assume there was an error */

#ifdef NETCONN
    if (netconn) {			/* For active network connections */
	debug(F100,"contti network","",0);
/*
  This is a horrible kludge.  If no data is available on the network,
  we have a busy loop that drives the VAX crazy.  The call to msleep()
  as a trailing catch-all would cure this somewhat, but then when data
  really is coming in, we'll have very jerky response.  The only reason
  for contti() to exist at all is that VMS C doesn't have a real fork()
  call, so the CONNECT command can't be implemented as two forks, both
  doing blocking reads, and making life easy on VMS.

  Anyway, even with the msleep() removed, as it is now, CONNECT-mode
  response is very jerky.  I can only guess that this is because the
  call to conchk() takes a long time -- like half a second or so?
*/
	while (1) {			/* A gross polling loop */
	    if (x = conchk()) {		/* Character at console? */
		debug(F101,"contti conchk","",x);
		if ((*c = coninc(0)) > -1) *src = 0;
		break;
	    } else if (x = ttchk()) {	/* Character on the network? */
		debug(F101,"contti ttchk","",x);
		if (x < 0) break;	/* Error checking network. */
		x = ttinc(0);		/* Net ok, get a character. */
		if (x < 0) break;	/* Error getting character. */
		*c = (cc = x & 0xff);	/* Watch out for sign extension. */
		*src = 1;
		break;
	    } /*** else msleep(50) ***/
	}
    } else				/* Not network */
#endif /* NETCONN */

    if (batch) {			/* Batch? */
	debug(F100,"contti batch","",0);
	if ((*c = getchar()) != EOF) {
	    *src = 0;
	} else {
	    *src = 1;
	    *c = ttinc(0);
	}
    } else {				/* Interactive but not network */
	debug(F100,"contti interactive","",0);
        if (!con_queued)
            if (!CHECK_ERR("contti: console sys$qio",
            sys$qio(CON_EFN, conchn, IO$_READVBLK, &coniosb, 0, 0,
                    &conch, 1, 0, 0, 0, 0))) return(-1);
        con_queued = 1;
        if (!tt_queued)
            if (!CHECK_ERR("contti: tty sys$qio",
            sys$qio(TTY_EFN, ttychn, IO$_READVBLK, &ttiosb, 0, 0,
                    &ttch, 1, 0, 0, 0, 0))) return(-1);
        tt_queued = 1;
        if (!CHECK_ERR("contti: sys$wflor",
            sys$wflor(CON_EFN, mask))) return(-1);
        if (!CHECK_ERR("contti: sys$readef",
            sys$readef(CON_EFN, &mask))) return(-1);		
        if (!(*src = ((mask & 1<<CON_EFN) ? 0 : 1))) {
            *c = conch;
            CHECK_ERR("contti: coniosb.status", coniosb.status);
            con_queued = 0;
        } else {
            *c = (ttprty ? ttch & 0177 : ttch);
            CHECK_ERR("contti: ttiosb.status", ttiosb.status);
            tt_queued = 0;
        }
        if (!(vms_status & 1)) *src = -1;
    }
    debug(F101,"contti returning *src","",*src);
    return((*src > -1) ? 1 : 0);
}

/*  C A N C I O  --  Cancel any pending i/o requests on the console or the
        comm. line.
*/

cancio()  {
    if (netconn) return;
    if (!batch) {
        CHECK_ERR("cancio: console sys$cancel",
            sys$cancel(conchn));
        CHECK_ERR("cancio: tty sys$cancel",
            sys$cancel(ttychn));
        con_queued = 0;
        tt_queued = 0;
    }
}

/* get_qio_maxbuf_size()
 *
 * Get maximum size of QIO that can occur without getting the dreaded
 * exceeded quota status.
 */

#ifndef SYI$_MAXBUF
#define SYI$_MAXBUF 4175
#endif

int get_qio_maxbuf_size(ttychn)
unsigned long int ttychn;
{
    unsigned char *tmpbuf;
    int unsigned long max=0;
    struct itmlst syiitm[] = { {2,SYI$_MAXBUF,&max,0},
    		{0,0,0,0}};

    if (!ttychn) return(-1);

    if (!CHECK_ERR("get_qio_maxbuf_size: sys$getsyiw",
	sys$getsyiw(	 0	/* efn */
			,0	/* csidadr */
			,0	/* nodename */
			,&syiitm /* itmlst */
			,&wrk_iosb /* iosb */
			,0	/* astadr */
			,0)))	/* astprm */
		exit(SS$_ABORT);		/* Fatal exit */

    if (!(tmpbuf = malloc(max)))
	return(0);

    for (; max > 0; max -= 16) {
	if (!test_qio(ttychn,max,tmpbuf)) /* (was &tmpbuf, caused crash) */
	{
	    free(tmpbuf);
	    return(max);
	}
    }

    free(tmpbuf);
    printf("\n%%CKERMIT-F-get_qio_maxbuf_size, Could not get maxbuf size\n");
    exit(SS$_ABORT);		/* Fatal exit */
}

int
test_qio(ttychn,max,dest)
unsigned long int ttychn;
long int max;
unsigned char *dest;
{
    static int trmmsk[2] = {0,0};

/*    trmmsk[1] = 1 << eol; */

    vms_status = sys$qiow(QIOW_EFN, ttychn, IO$_READVBLK|IO$M_TIMED,
			  &wrk_iosb, 0, 0, dest, max, 0, &trmmsk, 0, 0);
    return( !(vms_status & 1) ||
	(!(wrk_iosb.status & 1)) && wrk_iosb.status != SS$_TIMEOUT);
}


/*
 * Flush tt output buffer
 */

int
ttfluo() {

    long n=0;

#ifdef NETCONN
    if (netconn) return(0);
#endif /* NETCONN */

    if (!ttychn) return(-1);		/* Not open. */

    if (!CHECK_ERR("ttfluo: sys$qiow",
	sys$qiow(QIOW_EFN, ttychn, IO$_READVBLK|IO$M_TIMED|IO$M_PURGE,
		 &wrk_iosb, 0, 0, &n, 0, 0, 0, 0, 0))) {
	perror("flush failed");
	return(-1);
	}
    return(0);
}

/*  T T G M D M  --  Get modem signals  */
/*
 Looks for the modem signals CTS, DSR, and CTS, and returns those that are
 on in as its return value, in a bit mask as described for ttwmdm.  Returns:
 -3 Not implemented
 -2 if the line does not have modem control
 -1 on error.
 >= 0 on success, with a bit mask containing the modem signals that are on.
*/
int
ttgmdm() {

#ifdef NETCONN
    if (netconn) return(-2);
#endif /* NETCONN */

    return(-3);
}

/*
 * Return tty speed.  Speed is retreived from a qiow initially.
 * It is then changed only at user request.
 */
long
ttgspd() {
    extern int speed;
#ifdef NETCONN
    if (netconn) return(-1);		/* -1 if network connection */
#endif /* NETCONN */
    return(speed);
}


/*  T T S C A R R  --  Set ttcarr variable, controlling carrier handling.
 *
 *  0 = Off: Always ignore carrier. E.g. you can connect without carrier.
 *  1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect.
 *  2 = Auto: For "modem direct": The same as "Off".
 *            For real modem types: Heed carrier during connect, but ignore
 *                it anytime else.  Compatible with pre-5A C-Kermit versions.
 *
 * As you can see, this setting does not affect dialing, which always ignores
 * carrier (unless there is some special exception for some modem type).  It
 * does affect ttopen() if it is set before ttopen() is used.  This setting
 * takes effect on the next call to ttopen()/ttpkt()/ttvt().  And they are
 * (or should be) always called before any communications is tried, which
 * means that, practically speaking, the effect is immediate.
 *
 * Of course, nothing of this applies to remote mode (xlocal = 0).
 *
 * Someone has yet to uncover how to manipulate the carrier in the BSD
 * environment (or any non-termio using environment).  Until that time, this
 * will simply be a no-op for BSD.
 *
 * Note that in previous versions, the carrier was most often left unchanged
 * in ttpkt()/ttvt() unless they were called with DIALING or CONNECT.  This
 * has changed.  Now it is controlled by ttcarr in conjunction with these
 * modes.
 */
int
ttscarr(carrier) int carrier; {

    return(-1);
}


psuspend() {

    return(-1);
}
