Category : C Source Code
Archive   : KERMCU04.ZIP
Filename : CKVTIO.C

 
Output of file : CKVTIO.C contained in archive : KERMCU04.ZIP
#ifndef VMS
# error -- CKVTIO.C is used only on the VMS(tm) or OpenVMS(tm) Operating System
#endif /* VMS */

#ifdef __ALPHA
# define CKVTIO_OS_ARCH_STRING " OpenVMS(tm) AXP(tm)";
#else
# ifdef VAX
# module ckvtio "2.0-050"
# define CKVTIO_OS_ARCH_STRING " OpenVMS(tm) VAX(tm)";
# else
# error -- CKVTIO.C unknown architecture, neither VAX(tm) nor AXP(tm)
# endif /* VAX */
#endif /* __ALPHA */
/*
Module version number and date.
Also update the module number above accordingly.
*/
char *ckxv = "Communications I/O 2.0(050), 18 Nov 92";
/*
This is the default architecture and operating system herald string.
It is redetermined dynamically in sysinit() below.
*/
char *ckxsys = CKVTIO_OS_ARCH_STRING;

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

/*
Originally by:
S. Rubenstein, Harvard University Chemical Labs, 1985,
based on UNIX CKUTIO.C by Frank da Cruz, Columbia University.
*/

/* Edit History
*
* Cast of characters:
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* wb William Bader Lehigh University
* mab Mark Buda DEC
* fdc Frank da Cruz Columbia U
* HG Hunter Goatley Western KY U
* jh James Harvey Indiana / Purdue U
* tmk Terry Kennedy St Peters College
* MM Martin Minow DEC
* DS Dan Schullman DEC
* LT Lee Tibbert DEC
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 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 local/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 not supported on most Muxes 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 unsupported 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, addition of TGV MultiNet support.
* 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.
* 035 10-Jun-92 fdc Added code from Ray Hunter of The Wollongong Group to
* support both WIN/TCP and TGV Multinet. Network section
* of contti() redone to eliminate polling loop. It's
* infinitely faster.
* 036 11-Jun-92 tmk Fixed up edit 034 so 8-bit characters could be passed
* in connect mode.
* 037 19-Jun-92 fdc Totally rewrote all the serial input and mode-setting
* routines in this module to use nonblocking, fully
* buffered input and to share a common buffer. All
* serial-line input is localized to a single routine,
* txbufr(), which, in turn is known only to ttinc(). The
* other input routines, ttxin() and ttinl(), simply call
* ttinc(). ttchk() and ttflui() are totally cognizant of
* the buffer. ttinl() now recognizes packets with
* printable start characters and/or lacking terminators,
* so VMS C-Kermit can now engage in "Doomsday Kermit"
* protocol. ttvt() and ttpkt() were merged into a single
* new (static) routine, ttbin(), which no longer puts the
* device into PASALL mode (which defeats flow control).
* Added ttsndlb() to send a Long BREAK. Much fine-tuning,
* testing, and filling-in remains to be done, including
* (a) make ttopen() and ttclos() aware of LAT devices; (b)
* check remaining BYTLM quota before issuing a read, (c)
* integrate network and serial buffers, and much more.
* Anyway, this code seems to run faster than ever before,
* and for the first time I can actually use sliding
* windows AND long packets on my 8-year old MicroVAX-II.
* 038 28-Jun-92 tmk Additional work on edit 37, general cleanup of old defs.
* 039 1-Jul-92 wb Changes for VMS 4.4.
* 040 4-Jul-92 tmk Add modem signal support (ttgmdm routine).
* 041 4-Jul-92 tmk Add tgetent(), worker routine for el-cheapo curses.
* 042 4-Jul-92 jh Enable typeahead in ttbin().
* 043 21-Aug-92 fdc Make default flow control be KEEP instead of Xon/Xoff.
* 044 6-Sep-92 fdc Put default flow back to Xon/Xoff, but allow KEEP to be
* used to restore device's original flow-control setting.
* 045 23-Sep-92 fdc Add sleep(1) to tthang(). Seems to fix HANGUP command.
* Suggested by Lee Tibbert. Change ttbin() to use global
* flow variable rather than its flow parameter for setting
* flow control, to ensure the desired type of flow control
* is used during DIAL operations.
* 046 26-Sep-92 fdc Change sleep(1) in tthang() to sleep(3). Annoying but
* necessary. IO$M_HANGUP takes about 3 seconds, but the
* sys$qiow() function returns immediately instead of
* waiting for the action to complete.
* 047 08-Oct-92 HG Add call to sys$alloc in ttopen() to prevent user with
* SHARE from getting port in use. Some add'l cleanup.
* 048 12-Oct-92 LT Minor changes to support DEC TCP/IP (nee UCX).
* 049 25-Oct-92 fdc Adapt cancio() to DEC TCP/IP.
* Remove superfluous ttflui() call from ttpkt().
* Add code from Lee Tibbert to sysinit() to figure out OS
* and architecture name at runtime.
* 050 18-Nov-92 fdc Read from comm device in 1024-byte chunks, rather than
* trusting the qio_maxbuf_size. This should reduce BYTLM
* quota-exceeded errors. Suggested by tmk as a temporary
* workaround.
*/

/*
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 not executing interactively.
Used to ignore INT and QUIT signals, suppress messages, etc.
vms_status
Status returned by most recent VMS system service,
can 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"

#ifdef WINTCP
#include signal /* Interrupts */
#include stdio /* Unix Standard i/o */
#include setjmp /* Longjumps */
#include iodef
#include ttdef
#include tt2def
#include ssdef
#include descrip
#include dvidef
#include dcdef
#include devdef
#include time
#include syidef
#include starlet

#else /* Not WINTCP */

#include /* Unix Standard i/o */
#include /* Interrupts */
#include /* Longjumps */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

/* lt 1992-10-08 Begin
*/
#ifndef __ALPHA
# define void int
#endif /* __ALPHA */
/* lt 1992-10-08 End
*/

#if defined(VMS_V40) || defined(VMS_V42) || defined(VMS_V44) /* No starlet */
#define IO$_TTY_PORT 41
#else
#include
#endif /* (Old VMS) */
#endif /* WINTCP */


/* Macros */

#define xx_inc(timo) (--ttxbn>=0?ttxbuf[ttxbp++]:txbufr(timo))

/* Declarations */

time_t time();
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 */
int ttpflg = 0; /* Parity not sensed yet. */
int backgrd = 0; /* Running in "background" (no tty) */
static int ttpmsk = 0377;
int ttmdm = 0; /* Modem in use. */
int dfflow = FLO_XONX; /* Default flow control is Xon/Xoff */
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 /* TT$C_BAUD_38400 */

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 = 0; /* For timing statistics */

static char brkarray[] = { /* For simulating BREAK */

'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
};

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[80]; /* copy of open path for tthang */
static char lclnam[80]; /* Local device name */

static char tt_fulldevnam[65];
static struct dsc$descriptor_s tt_fulldevnam_d; /* Descriptor for line 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;

/* Network support */

#include "ckcnet.h" /* Network type symbols */
extern int ttnet; /* Network type */
static int network = 0; /* 1 if network connection */

/* Needed for parity fixes in edit 036 */
extern int parity; /* current parity setting */

/*
New buffered input scheme.
*/
#define TTXBUF

#ifdef TTXBUF
#define TTXBUFL RBSIZ /* Internal buffer size */

CHAR ttxbuf[TTXBUFL+1]; /* The buffer */
int ttxbp = 0, ttxbn = 0; /* Buffer pointer and count */

/*
T X B U F R

Read bytes from communication device into internal buffer ttxbuf[].
To be called only when input buffer is empty, i.e. when ttxbn == 0.

Other comm-device reading routines, like ttinc, ttinl, ttxin, should check
the internal buffer first, and call this routine for a refill if necessary.

When data is read successfully, the first character is returned and
the global buffer count, ttxbn, is set to the number of characters remaining
in ttxbuf after it, and the global buffer offset, ttxbp, is set to 1.

When data is not read successfully, -1 is returned indicating a timeout,
or -2 indicating disconnection.
*/
int
txbufr(timo) int timo; { /* TT Buffer Read */
int count;
int func; /* Read function code */
int mask; /* Event mask */
int vms_status; /* Read QIO return code */
static int trmmsk[2] = {0,0}; /* Terminal read break mask */
static int trmlong[8] = {0,0,0,0,0,0,0,0}; /* Break on nothing */

debug(F101,"txbufr entry, ttxbn","",ttxbn);
if (ttxbn > 0) { /* Should not be called */
debug(F101,"txbufr called with ttxbn","",ttxbn); /* if ttxbn > 0! */
ttxbn--;
return(ttxbuf[ttxbp++] & 0xff);
}
ttxbp = ttxbn = 0; /* Reset buffer pointer and count */
ttxbuf[0] = NUL;

if (timo < 0) /* Be safe */
timo = 0;
debug(F101,"txbufr timo","",timo);

func = IO$_READVBLK | IO$M_NOFILTR; /* Read virtual block, no filtering */
trmmsk[0] = sizeof(trmlong); /* No terminators */
trmmsk[1] = &trmlong; /* Keep all characters */

/*
We try to scoop up as many as we can in a nonblocking read (timed, but with
timeout value of 0). This is supposed to return immediately with up to
"count" characters placed in our buffer.
*/
count = TTXBUFL; /* Maximum characters to read */

#ifdef COMMENT
/*
This causes problems because we are not adjusting according to the CURRENT
BYTLM quota, but rather to the one that was obtained when Kermit started.
Since the quota is dynamic, it might have been reduced since then.
*/
if (count > qio_maxbuf_size)
count = qio_maxbuf_size;
#else
/*
So for now we use 1024, which tends to be smaller than the value obtained
above. Later, this should be changed to find out the remaining BYTLM quota
and use that instead. This size can be overridden at compile time by
defining the symbol...
*/
#ifndef CKV_IO_SIZE
#define CKV_IO_SIZE 1024
#endif /* CKV_IO_SIZE */

if (count > CKV_IO_SIZE)
count = CKV_IO_SIZE;
#endif /* COMMENT */

debug(F101,"txbufr 1 read","",count);
vms_status = sys$qiow(QIOW_EFN, ttychn, func|IO$M_TIMED, &wrk_iosb, 0, 0,
ttxbuf, count, 0, &trmmsk, 0, 0);
/*
* Did something _really_ bad happen?
*/
if (vms_status != SS$_NORMAL) {
debug(F101,"txbufr 1 serious error, status","",vms_status);
return(-2);
}

debug(F101,"txbufr 1 size","",wrk_iosb.size);
debug(F101,"txbufr 1 iosb","",wrk_iosb.status);
debug(F110,"txbufr 1 ttxbuf",ttxbuf,0);

/*
* How about a hangup?
*/
if (wrk_iosb.status == SS$_HANGUP) { /* Check for disconnection */
debug(F100,"txbufr 1 hangup","",0);
return(-2);
}

/*
* Did anything useful happen?
*/
if(wrk_iosb.size > 0) {
ttxbn = wrk_iosb.size; /* Set buffer count. */
ttxbn--; /* Less one for the one we return */
return(ttxbuf[ttxbp++] & 0xff); /* Return it, bump offset */
}

/*
* An unexpected status?
*/
if (wrk_iosb.status != SS$_TIMEOUT) {
debug(F101, "txbufr 1 unexpected iosb status", "", wrk_iosb.status);
return(-2); /* Call it a hangup */
}

/*
* If we didn't get any characters, then try a blocking, possibly timed,
* read for a single character. Since this routine will called again very

* soon, the first read with a zero timeout will have the rest of the user
* data in it. Thus, this isn't as inefficient as it first appears.
*/
if (timo > 0) func |= IO$M_TIMED;
debug(F101,"txbufr 2 read","",count);
vms_status = sys$qiow(QIOW_EFN, ttychn, func, &wrk_iosb, 0, 0,
ttxbuf, 1, timo, &trmmsk, 0, 0);
/*
* Did something _really_ bad happen?
*/
if (vms_status != SS$_NORMAL) {
debug(F101,"txbufr 2 serious error, status","",vms_status);
return(-2);
}

debug(F101,"txbufr 2 size","",wrk_iosb.size);
debug(F101,"txbufr 2 iosb","",wrk_iosb.status);
debug(F110,"txbufr 2 ttxbuf",ttxbuf,0);

/*
* How about a hangup?
*/
if (wrk_iosb.status == SS$_HANGUP) { /* Check for disconnection */
debug(F100,"txbufr 2 hangup","",0);
return(-2);
}

/*
* Did anything useful happen?
*/
if(wrk_iosb.size > 0) {
ttxbn = wrk_iosb.size; /* Set buffer count. */
ttxbn--; /* Less one for the one we return */
return(ttxbuf[ttxbp++] & 0xff); /* Return it, bump offset */
}

/*
* An unexpected status?
*/
if (wrk_iosb.status != SS$_TIMEOUT) {
debug(F101, "txbufr 2 unexpected iosb status", "", wrk_iosb.status);
return(-2); /* Call it a hangup */
}

/*
* Otherwise it's a timeout
*/
debug(F101, "txbufr 2 returning timeout", "", 0);
return(-1);
}

/* T T I N C -- Read a character from the communication device */
/*
ttinc() maintains an internal buffer to minimize system calls.
Returns the next character, or -1 if there is a timeout, or -2
on communications disconnect. Calls txbufr() to refill its buffer
when necessary.
*/
int
ttinc(timo) int timo; {
int x; unsigned char c;

#ifdef NETCONN
if (network)
return(netinc(timo));
#endif /* NETCONN */

debug(F101,"ttinc ttxbn","",ttxbn);
if (--ttxbn >= 0) { /* Something in internal buffer? */
c = ttxbuf[ttxbp++]; /* Yes, return next character. */
debug(F101,"ttinc returns c","",c);
return(c & 0xff);
} else if ((x = txbufr(timo)) < 0) { /* No, fill buffer */
debug(F101,"ttinc timed out","",x); /* Pass along failure. */
return(x);
} else { /* Success. */
debug(F101,"ttinc returns x","",x);
return(x & 0xff); /* Return the character */
}
}

/* 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, number of chars gotten on success.
*/
int
ttxin(n,buf) int n; char *buf; {
int i, x;

debug(F101,"ttxin","",n);

#ifdef NETCONN
if (network) {
for (i = 0; i < n; i++) {
if ((x = ttinc(0)) < 0) return(-1);
buf[i] = (char) x;
}
} else {
#endif /* NETCONN */
/* xx_inc() is a macro */
for (i = 0; i < n; i++) {
if ((x = xx_inc(0)) < 0) return(-1);
buf[i] = (char) x;
}
#ifdef NETCONN
}
#endif /* NETCONN */
buf[i] = NUL;
return(i);
}

/* T T F L U I -- Flush communication device input buffer */

int
ttflui() {
int n;
debug(F100,"ttflui","",0);
#ifdef NETCONN
if (network)
return(netflui());
#endif /* NETCONN */

ttxbn = ttxbp = 0; /* Flush internal buffer *FIRST* */
if ((n = ttchk()) > 0) {
debug(F101,"ttflui count","",n);
#ifdef NETCONN
if (network)
while ((n--) && ttinc(2) > -1) ; /* Don't worry, it's buffered. */
else
#endif /* NETCONN */
while ((n--) && xx_inc(2) > -1) ; /* Don't worry, it's buffered. */
}
return(0);
}

/* T T C H K -- Check how many bytes are waiting to be read */
/*
Returns number of bytes waiting, or -1 if connection has been dropped.
The number of bytes waiting includes those in our internal buffer plus
those in VMS's internal input buffer.
*/
int /* Check how many bytes are ready */
ttchk() { /* for reading from network */
static struct {
unsigned short count;
unsigned char first;
unsigned char reserved1;
long reserved2; } ttchk_struct;

#ifdef NETCONN
if (network) /* 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));
#ifdef DEBUG
debug(F101,"ttchk count","",(int)ttchk_struct.count);
if (ttchk_struct.count)
debug(F101,"ttchk first","",(int)ttchk_struct.first);
#endif /* DEBUG */
return(vms_status & 1 ? ttchk_struct.count + ttxbn : ttxbn);
}

#ifdef CTRLC
#undef CTRLC
#endif /* CTRLC */
#define CTRLC '\03'

/* T T I N L -- Read a record (up to break character) from comm line. */
/*
Reads up to "max" characters from the communication line, terminating on:

(a) the packet length field if the "turn" argument is zero, or
(b) on the packet-end character (eol) if the "turn" argument is nonzero
(c) two Ctrl-C's in a row

and returns the number of characters read upon success, or if "max" was
exceeded or the timeout interval expired before (a) or (b), returns -1.

The characters that were input are copied into "dest" with their parity bits
stripped if parity was selected. Returns the number of characters read.
Characters after the eol are available upon the next call to this function.

Since this function has grown to have its fingers so deeply into the
protocol, it is slated for removal: rpack() will take care of everything.
*/

int
ttinl(dest,max,timo,eol,start,turn) int max,timo,turn; CHAR *dest,eol,start; {
int x, y, c, i, j;
int ccn = 0; /* Control C counter */
int flag;
int cc;
unsigned char *cp;
int pktlen = -1;
int lplen = 0;
int havelen = 0;

debug(F101,"ttinl start","",start);
debug(F101,"ttinl turn","",turn);
i = j = flag = 0;
ttpmsk = (ttprty) ? 0177 : 0377; /* Set parity stripping mask. */
while (i < max) {
cc = network ? ttinc(timo) : xx_inc(timo); /* Read a byte */
if (cc < 0) /* Check for error */
return(-1);
if ((cc & 0x7f) == CTRLC) { /* Check for ^C^C */
if (++ccn > 1) { /* If we got 2 in a row, bail out. */
fprintf(stderr,"^C...\r\n"); /* Echo Ctrl-C */
return(-2);
}
} else ccn = 0; /* Not ^C, so reset ^C counter, */

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;
}
}
/*
If we have not been instructed to wait for a turnaround character, we
can go by the packet length field. If turn != 0, we must wait for the
end of line (eol) character before returning.
*/
#ifndef xunchar
#define xunchar(ch) (((ch) - 32 ) & 0xFF ) /* Character to number */
#endif /* xunchar */

if (i == 2) {
pktlen = xunchar(dest[1]);
havelen = (pktlen > 1);
debug(F101,"ttinl length","",pktlen);
} else if (i == 5 && pktlen == 0) {
lplen = xunchar(dest[4]);
} else if (i == 6 && pktlen == 0) {
pktlen = lplen * 95 + xunchar(dest[5]) + 5;
havelen = 1;
debug(F101,"ttinl length","",pktlen);
}
if (havelen && !turn && (i > pktlen+1)) { /* Use length field */
debug(F101,"ttinl break on length","",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);


debug(F101,"ttinl size","",x); /* Log the size */
dest[x] = '\0'; /* Terminate with null */

if (ttpflg++ == 0 && ttprty == 0) { /* Check and adjust the parity. */
if ((ttprty = parchk(dest,start,x)) > 0) {
debug(F000,"ttinl parchk senses parity","",ttprty);
ttpmsk = 0x7f;
for (i = 0; i < x; i++) /* Strip parity from this packet */
dest[i] &= 0x7f;
}
if (ttprty < 0) ttprty = 0; /* Restore if parchk error */
}
return(x); /* Return length */
}

#endif /* TTXBUF */

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);
debug(F100,s,"",0);
ermsg(msg);
}

/* S Y S I N I T -- System-dependent program initialization. */

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

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

sysinit() {
extern int speed;
extern char ttname[];
extern char *ckzsys;
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};

#define GETCKXSYS
/*
Get architecture and operating system name.
*/
#ifdef GETCKXSYS

#if defined(__ALPHA)

ckxsys = " OpenVMS AXP";

#elif !(defined(__VAX) || defined (VAX))
# error Unknown Hardware type, not VAX(tm) and not AXP (TM)
#else /* VAX */

/* OK, we have a VAX so what is the name of the OS? */

#ifndef SYI$_ARCH_NAME /* Should be in syidef.h but is not there yet */
# define SYI$_ARCH_NAME 4454
#endif /* SYI$_ARCH_NAME */

struct iosb_t {
short int status; /* System service status */
short int unused[3];
} iosb;

struct itmlst_t {
short unsigned int buffer_len; /* Buffer length */
short unsigned int item_code; /* Item code */
char*buffer; /* Where to write SYI info */
long unsigned int *ret_len; /* Pointer to returned length */
long unsigned int mbz; /* Must Be Zero */

} itmlst;

char arch_name[sizeof ("Alpha") - 1]; /* Architecture name */
long unsigned int ret_len; /* Returned length */
/*
$getsyi of "hw_arch" will fail prior to VMS 5.5. This failure indicates that
the OS name is "VAX/VMS" (sic). Use success or failure or $getsyi "hw_arch"
rather than the more straight forward $getsyi "node_swvers" because latter
is defined as four (4) characters and will get strange representing VMS
10.0.
*/

/* Default -- Not strictly correct but traditional & familiar... */
ckxsys = " VAX/VMS";

itmlst.buffer_len = sizeof (arch_name);
itmlst.item_code = SYI$_ARCH_NAME;
itmlst.buffer = arch_name;
itmlst.ret_len = &ret_len;
itmlst.mbz = 0;

if ((sys$getsyiw (0, 0, 0,
&itmlst,
&iosb,
0, 0) & 1) == 1)
if ((iosb.status & 1) == 1)
ckxsys = " OpenVMS VAX";

ckzsys = ckxsys; /* Same deal for file module */

#endif /* OS Type */
#endif /* GETCKXSYS */

/*
* 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 /* COMMENT */
}
if (ttychn) /* if comms line already opened */
return(0); /* (how could it be???) */

if (!conchn) { /* Get console channel */
struct dsc$descriptor_s devnam =
{sizeof(dftty)-1,DSC$K_DTYPE_T,DSC$K_CLASS_S,dftty};
conchn = vms_assign_channel(&devnam);
}
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,80);

/* Initialize descriptor */
tt_fulldevnam_d.dsc$b_dtype = DSC$K_DTYPE_T;
tt_fulldevnam_d.dsc$b_class = DSC$K_CLASS_S;

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,&tt_fulldevnam,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 (network && ttyfd > -1) { /* if device already opened */
if (strncmp(ttname,ttnmsv,80)) /* 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);
network = 1;
x = tn_ini(); /* Initialize telnet protocol. */
ttnet = modem;
} else network = 0;
return(x);
}
#endif /* NETCONN */

if (ttychn) return(0); /* Close channel if open */

devnam.dsc$w_length = strlen(ttname);
devnam.dsc$a_pointer = ttname;
sys$getdviw(0, 0, &devnam, &dviitm, &wrk_iosb, 0, 0, 0);
tt_fulldevnam[65] = '\0'; /* Make sure it has an end.... */

if (devclass != DC$_TERM) { /* Is it a terminal? */
fprintf(stderr,
"%%CKERMIT-W-NOTTERM, %s is not a terminal\n",ttname);
return(-1);
}
if (!(devchar & DEV$M_AVL)) { /* Is it available? */
fprintf(stderr,
"%%CKERMIT-W-NOTAVAL, %s is not available\n",tt_fulldevnam);
return(-5);
}
if (!(devsts & UCB$M_ONLINE)) { /* Is it online? */
fprintf(stderr,
"%%CKERMIT-W-OFFLINE, %s is not online\n",tt_fulldevnam);
return(-5);
}
ttmdm = modem; /* Make this available to other fns */
xlocal = *lcl; /* Make this available to other fns */

/*
* Set up the tt_fulldevnam_d descriptor for use by ttclos() later.
*/
tt_fulldevnam_d.dsc$w_length = strlen(tt_fulldevnam);
tt_fulldevnam_d.dsc$a_pointer = tt_fulldevnam;

/*
* Try to allocate the device. This is necessary to avoid just assigning
* a channel with SHARE privilege and interfering with someone else using
* the terminal.
*/
if (!CHECK_ERR("vms_allocate_device: sys$alloc",
sys$alloc(&tt_fulldevnam_d, 0, 0, 0, 0))) return(-1);

ttychn = vms_assign_channel(&devnam); /* Get a channel for it. */

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 quota exceeded
* status returned. When doing a QIO that has a larger buffer than
* MAXBUF, exceeded quota wil be returned.
*
* 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);

strncpy(ttname,tt_fulldevnam,80); /* Copy true name back to main pgm */

strcpy(ttnmsv,ttname); /* Open, keep copy of name locally. */
ttxbn = ttxbp = 0; /* Initialize input buffer */

/* 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,80) == 0) ? 0 : 1;
else
xlocal = 1; /* If not a term, then we must be local */
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);
}

#ifdef COMMENT
/*
Old version.
*/
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);
}
#else
/*
New version from Hunter Goatley.
*/
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(ttname, &channel, 0, 0))) return(0);
return(channel);
}
#endif /* COMMENT */

/* T T C L O S -- Close the communication device. */

int
ttclos() {
#ifdef NETCONN
if (network) { /* Network connection. */
debug(F100,"ttclos closing net","",0);
netclos(); /* Close it. */
network = 0;
return(0);
}
#endif /* NETCONN */
if (!ttychn) return(0); /* Wasn't open. */
/*
Observations indicate that it can take 20-30 seconds for DTR to drop
after closing the device. Perhaps a call to tthang() should go here.
*/
ttres(); /* Reset modes. */

/*
Assume it's a LAT device and try to do a LAT disconnect on it.
If it fails, then it's not a LAT device and no harm is done.
*/
#ifdef IO$M_LT_DISCON
vms_status = sys$qiow(QIOW_EFN, ttychn, IO$_TTY_PORT|IO$M_LT_DISCON,
&wrk_iosb, 0, 0, 0, 0, 0, 0, 0, 0);
debug(F101, "ttclos LAT disconnect, status", "", vms_status);
debug(F101, "ttclos LAT disconnect, iosb", "", wrk_iosb.status);
#else
debug(F100, "ttclos LAT disconnect not supported", "", 0);
#endif /* IO$M_LT_DISCON */
if (!CHECK_ERR("ttclos: sys$dassgn",
sys$dassgn(ttychn))) return(-1);
sys$dalloc (&tt_fulldevnam_d, 0); /* Deallocate the device */
ttychn = 0; /* Mark it as closed. */
return(0);
}


/* T T R E S -- Restore terminal to its original modes. */

int
ttres() { /* Restore the tty to normal. */
#ifdef NETCONN
if (network) 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 B I N -- Code shared by ttpkt() and ttvt() */
/*
Puts communication device in "binary" mode. In VMS there's no distinction
between device modes for terminal connection, packet operation, and dialing.
*/
static int
ttbin(speed, xflow, xparity) int speed, xflow, xparity; {
int s;
extern int flow; /* Global flow control variable */

#ifdef NETCONN
if (network) return(0); /* Nothing to do on net connections */
#endif /* NETCONN */
if (!ttychn) return(-1); /* Not open. */

ttspeed = speed; /* Keep local copies of arguments */
if (xflow != FLO_DIAL && ttflow != FLO_DIAX)
ttflow = xflow; /* for other CKVTIO routines. */
if (xparity > -1) {
ttprty = xparity;
ttpflg = 0; /* Parity not sensed yet */
ttpmsk = ttprty ? 0177 : 0377; /* Parity stripping mask */
debug(F101,"ttbin ttprty","",ttprty);
}
ttraw = ttold; /* Get a fresh copy of this */

if ((s = ttsspd(speed/10)) < 0) /* Get internal speed code */
s = 0;

/*
* Log original terminal settings for debugging purposes
*/
debug(F101, "original ttraw.basic", "", ttraw.basic);
debug(F101, "original ttraw.extended", "", ttraw.extended);

/*
* Settings based on call parameters flow-control and parity...
* NOTE: we are using the GLOBAL copy of flow, not our parameter here.
* This is because the parameter might be FLO_DIAL or FLO_DIALX, which
* is not flow control at all.
*/
if (flow == FLO_XONX) { /* FLOW = XON/XOFF */
ttraw.basic |= (TT$M_HOSTSYNC|TT$M_TTSYNC);
} else if (flow == FLO_NONE) { /* FLOW = NONE */
ttraw.basic &= ~(TT$M_HOSTSYNC|TT$M_TTSYNC);
} else if (flow == FLO_KEEP) { /* FLOW = KEEP */
/*
* Put flow-control paramaters back the way we found them when
* the device was first opened.
*/
if (ttold.basic & TT$M_HOSTSYNC)
ttraw.basic != TT$M_HOSTSYNC;
else
ttraw.basic &= ~TT$M_HOSTSYNC;
if (ttold.basic & TT$M_TTSYNC)
ttraw.basic != TT$M_TTSYNC;
else
ttraw.basic &= ~TT$M_TTSYNC;
/*
NOTE: any other FLOW-related parameters should also be handled here.
READSYNC? And especially if DEC ever implements RTS/CTS or other
hardware flow control for (Open)VMS.
*/
}

/*
EIGHTBIT setting depends on GLOBAL copy of parity variable, not our
parameter.
*/
if (parity == 0)
ttraw.basic |= TT$M_EIGHTBIT; /* Allow 8-bit data if no parity */
else /* Otherwise */
ttraw.basic &= ~TT$M_EIGHTBIT; /* 7-bit data. */

ttraw.basic |= TT$M_NOECHO; /* Turn off echo */
ttraw.basic |= TT$M_NOBRDCST; /* Turn off broadcasts */
ttraw.basic &= ~TT$M_NOTYPEAHD; /* Enable type-ahead */
ttraw.extended |= TT2$M_PASTHRU; /* Enable pass-through mode */
ttraw.extended |= TT2$M_ALTYPEAHD; /* Big type-ahead buffers */

/*
* Report what we did so we can check for problems
*/
debug(F101, "ttraw.basic", "", ttraw.basic);
debug(F101, "ttraw.extended", "", ttraw.extended);

vms_status = sys$qiow(QIOW_EFN, ttychn, IO$_SETMODE, &wrk_iosb, 0, 0,
&ttraw, sizeof(ttraw), s, 0, 0, 0);

if (vms_status != SS$_NORMAL) { /* Error queuing request */
print_msg("ttbin: sys$qiow");
return(-1);
}
if (wrk_iosb.status != SS$_NORMAL) { /* Error executing request */
vms_status = wrk_iosb.status;
print_msg("ttbin: sys$qiow(iosb)");
return(-1);
}
debug(F100,"ttbin ok","",0);
return(0); /* All OK */
}

/* T T P K T -- Condition the communication device for packets. */

#ifdef COMMENT
#define DIALING 4 /* Flags (via flow) for modem handling */
#define CONNECT 5 /* NOT YET IMPLEMENTED IN VMS! */
#endif /* COMMENT */

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

int
ttpkt(speed,flow,parity) int speed, flow, parity; {
int x;
debug(F101,"ttpkt flow","",flow);
x = ttbin(speed,flow,parity); /* Put device in binary mode */
debug(F101,"ttpkt ttbin","",x);
return(x);
}

/* T T V T -- Condition communication device terminal connection. */

int
ttvt(speed,flow) int speed, flow; {
int x;
debug(F101,"ttvt flow","",flow);
if ((x = ttbin(speed,flow,-1)) > -1) /* Put device in binary mode */
tvtflg = 1;
debug(F101,"ttvt ttbin","",x);
return(x);
}

/* 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 (network) 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 (network) return(0);
#endif /* NETCONN */

if (cps <= 0) /* 026 Unknown cps fails */
return (-1);
for (s = 0; ttspeeds[s].line && (ttspeeds[s].line != cps); s++) ;
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);
}
}


/* 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 = backgrd = 1; /* means running in background */
return;
}
#endif /* COMMENT */

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 /* COMMENT */
conif = 0;
}

/* 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 /* IO$M_BREAKTHRU */

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

#ifdef NETCONN
debug(F101,"ttol network","",network);
if (network) {
x = nettol(s,n); /* Call network package. */
} else {
#endif /* NETCONN */
debug(F101,"ttol 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; {
#ifdef NETCONN
if (network) {
return(nettoc(c));
} else {
#endif /* NETCONN */
debug(F101,"ttoc char","",c);
if (!ttychn) {
debug(F100,"ttoc ttychn not open","",0);
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 _ C A N C E L -- Cancel i/o on tty channel if not complete */

VOID
tt_cancel() {
int mask;
#ifdef NETCONN
if (network) 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;
}

/* S N D B R K -- Send a BREAK signal of the given length. */

int
sndbrk(msec) int msec; {
int long x = 0;
int brklen;
struct iosb_struct tmp_ttiosb;
struct tt_mode ttchr;
#ifndef TT$M_BREAK /* For old VMS with no BREAK... */
/*
Note: 110 is used instead of 50, because 50 is not supported by all
VAX serial port controllers.
*/
#define BRKSPD = 110 /* Speed for simulating BREAK */
#define BRKSYM = TT$C_BAUD_110; /* VMS symbol for this speed */
#endif /* TT$M_BREAK */

#ifdef NETCONN
if (network) /* Send network BREAK */
return(netbreak()); /* Length doesn't matter */
#endif /* NETCONN */

if (!ttychn) return(-1); /* SET LINE not done. */
debug(F101,"sndbrk msec","",msec);

tt_cancel(); /* Cancel I/O */

#ifndef TT$M_BREAK /* VMS doesn't have BREAK function */

/* Send the right number of NULs at BREAK-simulation speed... */

brklen = ( BRKSPD * 1000 ) / ( msec * 10 ); /* Calculate number of chars */
if (brklen > sizeof(brkarray)) brklen = sizeof(brkarray);
debug(F101,"sndbrk speed","",BRKSPD);
debug(F101,"sndbrk brklen","",brklen);
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), BRKSYM, 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, (char *) brkarray, brklen, 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);
x = TT$M_BREAK; /* Break signal on */
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(msec); /* Sleep requested amount of time */
x = 0; /* Break signal off */
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 S N D B -- Send a BREAK signal */

int
ttsndb() {
return(sndbrk(275));
}

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

int
ttsndlb() {
return(sndbrk(1500));
}

/* 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() {
if (!xlocal) return(0); /* Only on local connections. */

#ifdef NETCONN
if (network) { /* 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(); /* Cancel pending i/o. */
/*
This is NOT listed in the VMS Terminal Driver as one of the functions
that does NOT work with LAT devices.
*/
debug(F101,"tthang 1","",gtimer());
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);
/*
The following 3-second sleep is required because the sys$qiow() returns
immediately, about 2.5 seconds before VMS brings DTR back up. Without this
sleep(), DIAL does not work at all if DIAL HANGUP is ON, and, worse,
subsequent operations on the device can hang the Kermit process
uninterruptibly.
*/
sleep(3);
debug(F101,"tthang 2","",gtimer());
return(1);
}

/* 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();
debug(F101,"msleep ok","",m);
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);
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 /* bogus */
#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 = backgrd = 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.
*/
cccbrk.extended |= TT2$M_PASTHRU | TT2$M_ALTYPEAHD;
if (parity)
cccbrk.basic |= TT$M_NOECHO;
else
cccbrk.basic |= TT$M_NOECHO | TT$M_EIGHTBIT;
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 */
ccraw.extended |= TT2$M_PASTHRU | TT2$M_ALTYPEAHD;
if (parity)
ccraw.basic |= TT$M_NOECHO;
else
ccraw.basic |= TT$M_NOECHO | TT$M_EIGHTBIT;
ccraw.basic &= ~(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 || backgrd) 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 /* COMMENT */
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.
*/
#ifdef VMSTCPIP
/*
* Network/console read posted?
*/
static int nettty_queued = 0;
static int netcon_queued = 0;
#endif /* VMSTCPIP */

int
contti(c, src) int *c, *src; {

#ifndef VMSTCPIP
int mask = 1< int x; unsigned char cc;

#else /* VMSTCPIP */

#define NET_EFN 7 /* Network event flag */

int mask; /* Event flag mask */

static CHAR concc; /* Console and network data */
static CHAR netcc;

static struct iosb_struct net_iosb;
static struct iosb_struct con_iosb; /* IO status blocks */

/*
Buffered network data, count, next character. Declared in CKCNET.C ...
*/
extern CHAR ttibuf[];
extern int ttibn;
extern int ttibp;
#endif /* VMSTCPIP */

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

#ifdef VMSTCPIP
if (network) { /* For active network connections */
debug(F100,"contti network","",0);

if (ttibn > 0) {
/*
* Handle the case where data remains in our "internal" buffer.
* We need to:
*
* -- Handle the console keyboard (is a character ready?)
* -- Return one character from the network buffer if not
*
* Post a new console read if necessary
*/
if (!netcon_queued) {
if (!CHECK_ERR("contti: console sys$qio",
sys$qio(CON_EFN, conchn, IO$_READVBLK,
&con_iosb, 0, 0, &concc, 1,
0, 0, 0, 0))) return(-1);
netcon_queued = 1;
}
/*
* Console character ready?
*/
(void) sys$readef(CON_EFN, &mask);

if (mask & (1 << CON_EFN)) {
/*
* Yes, return it
*/
netcon_queued = 0;

if (!CHECK_ERR("contti: con_iosb.status",
con_iosb.status)) return(-1);

*c = concc & 0xff;
*src = 0;
return(1);
}
/*
* No console data; return buffered network character
*/
ttibn--;
*c = ttibuf[ttibp++];
*src = 1;
return(1);
}
/*
* No buffered data; post network and console reads
*/
if (!nettty_queued) {
/* -lt. 1992-09-14 begin */
/* All the event flag numbers should be obtained using lib$get_ef().
* Using hard coded numbers, especially < 31 is tres dangerous!!!
* Be careful, one must also change the event flag cluster used by
* sys$readef. It is *not* just a simple matter of changing a few #defines.
*
* At least for DEC TCP/IP Services, socket calls return a proper file
* descriptor (fd). OpenVMS system services require a channel
* (from sys$assign). The two are *not* the same. The call vaxc$get_sdc()
* maps from a DEC TCP/IP fd to a channel.
*
* This is "gag me with a spoon" code, but it gets thing up and running.
*
*/
#ifdef DEC_TCPIP

{
static int last_ttyfd = -1;
static short int net_chan = -1;

if (ttyfd != last_ttyfd){
last_ttyfd = ttyfd;
#ifdef __DECC
net_chan = (short) decc$get_sdc(last_ttyfd);
#else
#ifdef VAXC
net_chan = vaxc$get_sdc(last_ttyfd);
#else
# error CALL TO GET_SDC requires DECC or VAXC compiler!
#endif /* VAXC */
# endif /* DECC */
}

if (!CHECK_ERR("contti: network sys$qio",
sys$qio(NET_EFN, net_chan, IO$_READVBLK, &net_iosb, 0, 0,
&netcc, 1, 0, 0, 0, 0))) return(-1);
}

#else /* Not DEC_TCPIP */

if (!CHECK_ERR("contti: network sys$qio",
sys$qio(NET_EFN, ttyfd, IO$_READVBLK, &net_iosb, 0, 0,
&netcc, 1, 0, 0, 0, 0))) return(-1);
#endif /* DEC_TCPIP */
nettty_queued = 1;
}

if (!netcon_queued) {
if (!CHECK_ERR("contti: console sys$qio",
sys$qio(CON_EFN, conchn, IO$_READVBLK, &con_iosb,
0, 0, &concc, 1, 0, 0, 0, 0))) return(-1);
netcon_queued = 1;
}
/*
* Wait for a character
*/
mask = (1 << CON_EFN) | (1 << NET_EFN);

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 (mask & (1 << CON_EFN)) {
/*
* Console
*/
if (!CHECK_ERR("contti: con_iosb.status",
con_iosb.status)) return(-1);

*c = concc & 0xff;
*src = 0;
netcon_queued = 0;

} else if (mask & (1 << NET_EFN)) {
/*
* Network
*/
if (!(net_iosb.status & 1)) {
/*
* Network read error
*/
#ifdef WINTCP
_$set_vaxc_error(SS$_NORMAL, net_iosb.status);
win$perror("contti: net_iosb.status");
#else
#ifdef MULTINET
#ifdef COMMENT
/*
When user hangs up, this prints an unnecessary scary message,
like "Operation would block."
*/
socket_perror("contti: net_iosb.status");
#endif /* COMMENT */
#endif /* MULTINET */
#endif /* WINTCP */
return(-1);
}

if (net_iosb.size == 0) {
/*
* Handle reset from remote
*/
return(-1);
}
*c = netcc & 0xff;
*src = 1;
nettty_queued = 0;
}
} else /* Not network */
#endif /* VMSTCPIP */

/*
Should we worry about a network connection that's running under BATCH ?
*/
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 */

#ifdef TTXBUF
if (ttxbn > 0) { /* Buffered port chars available */

/* Post a read on the console if one is not posted already */

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;
}

/* See if a console character has been read and if so, return it. */

(void) sys$readef(CON_EFN, &mask);
if (mask & (1 << CON_EFN)) {
con_queued = 0;
if (!CHECK_ERR("contti: coniosb.status",
coniosb.status))
return(-1);
*c = conch & 0xff;
*src = 0;
return(1);
}

/* No console character, so return buffered port character */

*c = ttinc(0);
*src = 1;
return(1);
}

/* No buffered port data; post both network and console reads... */

#endif /* TTXBUF */

mask = 1<
debug(F101,"contti interactive mask","",mask);

if (!con_queued) { /* Console read not 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;
debug(F100,"contti con_queued","",0);
}
if (!tt_queued) { /* Port read not 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;
debug(F100,"contti tt_queued","",0);
}

/* Wait for one of the queued reads to complete */

if (!CHECK_ERR("contti: sys$wflor",
sys$wflor(CON_EFN, mask))) return(-1);
debug(F100,"contti sys$wflor ok","",0);

/* Read the event flags to see which read was completed */

if (!CHECK_ERR("contti: sys$readef",
sys$readef(CON_EFN, &mask))) return(-1);
debug(F100,"contti sys$readef ok","",0);


/* Return the character with the appropriate source (src) indicator */

if (!(*src = ((mask & 1< *c = conch;
CHECK_ERR("contti: coniosb.status", coniosb.status);
con_queued = 0;
} else {
*c = (ttprty ? ttch & 0177 : ttch);
if (ttiosb.status == SS$_HANGUP) {
fprintf(stderr,"\n%%CKERMIT-F-HANGUP, data set hang-up");
*src = -1;
return(1);
}
CHECK_ERR("contti: ttiosb.status", ttiosb.status);
tt_queued = 0;
}
if (!(vms_status & 1)) *src = -1;
}
debug(F101,"contti *src","",*src);
return((*src > -1) ? 1 : 0);
}

/*
C A N C I O
Cancel pending I/O requests on console and communication device.
*/
cancio() {
#ifdef NETCONN
if (network) {
#ifdef VMSTCPIP
#ifdef DEC_TCPIP
short int net_chan = -1;
#ifdef __DECC
net_chan = (short) decc$get_sdc(ttyfd);
#else
#ifdef VAXC
net_chan = vaxc$get_sdc(ttyfd);
#else
# error CALL TO GET_SDC requires DECC or VAXC compiler!
#endif /* VAXC */
# endif /* DECC */
if (nettty_queued) (void) sys$cancel(net_chan);
#else
if (nettty_queued) (void) sys$cancel(ttyfd);
#endif /* DEC_TCPIP */
if (netcon_queued) (void) sys$cancel(conchn);

netcon_queued = 0;
nettty_queued = 0;
return;
#else /* Not VMSTCPIP */
return;
#endif /* VMSTCPIP */
}
#endif /* NETCONN */

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 /* SYI$_MAXBUF */

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 (network) 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() {
struct {
unsigned char type;
unsigned char spare1;
unsigned char modem;
unsigned char spare2;
unsigned long filler;
} mdminfo;
int retval;

#ifdef NETCONN
if (network) return(-2);
#endif /* NETCONN */
vms_status = sys$qiow(QIOW_EFN, ttychn, IO$_SENSEMODE|IO$M_RD_MODEM,
&wrk_iosb, 0, 0, &mdminfo, 0, 0, 0, 0, 0);

if (vms_status != SS$_NORMAL) {
debug(F101,"ttgmdm serious error, status","",vms_status);
return(-1);
}

debug(F101,"ttgmdm iosb","",wrk_iosb.status);
debug(F101,"ttgmdm type","",mdminfo.type);
debug(F101,"ttgmdm modem","",mdminfo.modem);

if (wrk_iosb.status != SS$_NORMAL) {
debug(F101,"ttgmdm iosb error, status","",wrk_iosb.status);
return(-1);
}

#ifdef DT$_LAT
if (mdminfo.type == DT$_LAT) {
debug(F101,"ttgmdm LAT port, no modem control","",0);
return(-2);
}
#endif /* DT$_LAT */

if (mdminfo.type == 0) {
debug(F101,"ttgmdm unknown driver, modem","",mdminfo.modem);
return(-2);
}

retval = BM_DTR | BM_RTS; /* Not visible, set by TTDRIVER */
if (mdminfo.modem & TT$M_DS_CTS)
retval |= BM_CTS;
if (mdminfo.modem & TT$M_DS_DSR)
retval |= BM_DSR;
if (mdminfo.modem & TT$M_DS_CARRIER)
retval |= BM_DCD;
if (mdminfo.modem & TT$M_DS_RING)
retval |= BM_RNG;
return(retval);
}

/*
* 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 (network) 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);
}

#ifdef CK_CURSES
/*
tgetent() support for VMS curses emulation.
Used by all three VMS fullscreen methods.
Called from "SET FILE DISPLAY FULLSCREEN" in ckuus7.c.
*/
int isvt52 = 0; /* VT52/VT1xx flag */

int
tgetent(lp, term) char *lp, *term; {
debug(F101,"tgetent terminal type","",ccold.type);
debug(F101,"tgetent terminal extended","",ccold.extended);

if ((ccold.type == DT$_VT5X) || (ccold.type == DT$_VT55)) {
debug(F100,"tgetent VT5x","",0);
isvt52 = 1;
return(1);
}
if ((ccold.extended & TT2$M_ANSICRT) == TT2$M_ANSICRT) {
debug(F100,"tgetent ANSICRT","",0);
isvt52 = 0;
return(1);
}
if ((ccold.extended & TT2$M_DECCRT) == TT2$M_DECCRT) {
debug(F100,"tgetent DECCRT","",0);
isvt52 = 0;
return(1);
}
return(0); /* Not a supported terminal type */
}
#endif /* CK_CURSES */




  3 Responses to “Category : C Source Code
Archive   : KERMCU04.ZIP
Filename : CKVTIO.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/