char *ckxv = "VMS tty I/O, 1.0(012), 11 Jul 85"; /* C K V T I O -- Terminal and Interrupt Functions for VAX/VMS */ /* Edit History * 012 11 Jul 85 FdC Add gtimer(), rtimer() for timing statistics. * 011 5 Jul 85 DS Treat hangup of closed line as success. * 010 25 Jun 85 MM Added sysinit() to open console. * 009 18 Jun 85 FdC Move def of CTTNAM to ckcdeb.h so it can be shared. * 008 11 Jun 85 MM Fix definition of CTTNAM * * 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. * * 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. */ /* C-Kermit interrupt, terminal control & i/o functions for VMS systems */ /* S. Rubenstein, Harvard University Chemical Labs */ /* (c) 1985 President and Fellows of Harvard College */ char *ckxsys = " Vax/VMS"; /* Variables available to outside world: dftty -- Pointer to default tty name string, like "/dev/tty". dfloc -- 0 if dftty is console, 1 if external line. 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. */ /* 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 "ckcker.h" #include /* Unix Standard i/o */ #include /* Interrupts */ #include /* Longjumps */ #include #include #include #include #include #include #include "ckcdeb.h" /* Formats for debug() */ /* Declarations */ long time(); /* Get current time in secs */ /* dftty is the device name of the default device for file transfer */ /* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */ char *dftty = "TT:"; int dfloc = 0; /* Default location is remote */ int dfprty = 0; /* Parity (0 = none) */ int dfflow = 1; /* Xon/Xoff flow control */ int batch = 0; /* Assume interactive */ int ckxech = 0; /* 0 if system normally echoes console characters, else 1 */ int vms_status; /* Used for system service return status */ /* Structures used within this module */ struct tt_mode { char class, type; short width; int basic : 24; char length; long extended; }; struct iosb_struct { short status, size, terminator, termsize; }; /* Declarations of variables global within this module */ static int conif = 0, /* Console interrupts on/off flag */ cgmf = 0, /* Flag that console modes saved */ 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 */ ttch; /* tty input character buffer */ static struct iosb_struct coniosb, ttiosb; static char escchr; /* Escape or attn character */ static struct tt_mode ttold, ttraw, tttvt, /* for communication line */ ccold, ccraw, cccbrk; /* and for console */ static long tcount; /* For timing statistics */ /* Event flags used for I/O completion testing */ #define CON_EFN 1 #define TTY_EFN 2 #define TIM_EFN 3 #define SUCCESS(x) (((vms_status = (x)) & 7) == 1) #define CHECK_ERR(s,x) (SUCCESS(x) ? 1 : print_msg(s)) #if 0 /* These aren't used */ static int inbufc = 0; /* stuff for efficient raw line */ static int ungotn = -1; /* pushback to unread character */ #endif /* P R I N T _ M S G -- Log an error message from VMS */ print_msg(s) char *s; { struct dsc$descriptor_s b; short blen; char buf[80], msg[120]; b.dsc$w_length = sizeof buf; b.dsc$b_dtype = DSC$K_DTYPE_T; b.dsc$b_class = DSC$K_CLASS_S; b.dsc$a_pointer = buf; SYS$GETMSG(vms_status, &blen, &b, 0, 0); buf[blen] = '\0'; sprintf(msg, "%s: %s\n", s, buf); ermsg(msg); } /* S Y S I N I T -- System-dependent program initialization. */ sysinit() { if (conchn == 0) conchn = vms_assign_channel("SYS$INPUT:"); return(0); } /* T T O P E N -- Open a tty for exclusive access. */ /* Returns 0 on success, -1 on failure. */ ttopen(ttname,lcl,modem) char *ttname; int *lcl, modem; { if (ttychn != 0) return(0); /* If already open, ignore this call */ ttychn = vms_assign_channel(ttname); if (ttychn == 0) return(-1); if (!CHECK_ERR("ttopen: SYS$QIOW", SYS$QIOW(0, ttychn, IO$_SENSEMODE, 0, 0, 0, &ttold, sizeof ttold, 0, 0, 0, 0))) return(-1); /* Tell whether local or remote */ *lcl = (strcmp(ttname,CTTNAM) == 0) ? 0 : 1; tttvt = ttold; ttraw = ttold; /* * Possibly add "purge I/O" call here. */ return(0); } vms_assign_channel(ttname) char *ttname; { int channel; struct dsc$descriptor_s d; d.dsc$w_length = strlen(ttname); d.dsc$a_pointer = ttname; d.dsc$b_class = DSC$K_CLASS_S; d.dsc$b_dtype = DSC$K_DTYPE_T; if (!CHECK_ERR("vms_assign_channel: SYS$ASSIGN", SYS$ASSIGN(&d, &channel, 0, 0))) return(0); else return(channel & 0xFFFF); } /* T T C L O S -- Close the TTY, releasing any lock. */ ttclos() { if (ttychn == 0) 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. */ ttres() { /* Restore the tty to normal. */ if (ttychn == 0) return(-1); /* Not open. */ sleep(1); /* Wait for pending i/o to finish. */ debug(F101,"ttres, ttychn","",ttychn); if (!CHECK_ERR("ttres: SYS$QIOW", SYS$QIOW(0, ttychn, IO$_SETMODE, 0, 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. */ ttpkt(speed,flow) int speed, flow; { extern char ttname[]; int s; if (ttychn == 0) return(-1); /* Not open. */ s = ttsspd(speed); /* Check the speed */ #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 ttraw.basic |= TT$M_NOECHO | TT$M_EIGHTBIT; ttraw.basic &= ~TT$M_WRAP & ~TT$M_HOSTSYNC & ~TT$M_TTSYNC; if (!CHECK_ERR("ttpkt: SYS$QIOW", SYS$QIOW(0, ttychn, IO$_SETMODE, 0, 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 */ ttvt(speed,flow) int speed, flow; { extern char ttname[]; int s; if (ttychn == 0) return(-1); /* Not open. */ s = ttsspd(speed); /* Check the speed */ #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 ttraw.basic |= TT$M_NOECHO | TT$M_EIGHTBIT | TT$M_HOSTSYNC | TT$M_TTSYNC; ttraw.basic &= ~TT$M_WRAP; if (!CHECK_ERR("ttvt: SYS$QIOW", SYS$QIOW(0, ttychn, IO$_SETMODE, 0, 0, 0, &ttraw, sizeof ttraw, s, 0, 0, 0))) return(-1); } /* T T S S P D -- Return the internal baud rate code for 'speed'. */ int speeds[] = { 110, 150, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 0 } ; static int speedcodes[] = { TT$C_BAUD_110, TT$C_BAUD_150, TT$C_BAUD_300, TT$C_BAUD_600, TT$C_BAUD_1200, TT$C_BAUD_1800, TT$C_BAUD_2400, TT$C_BAUD_4800, TT$C_BAUD_9600, TT$C_BAUD_19200 } ; int ttsspd(speed) int speed; { int s; char msg[50]; if (speed < 0) /* 006 Unknown speed fails */ return (0); for (s = 0; speeds[s] != 0 && speeds[s] != speed; s++) ; if (speeds[s] != 0) return(speedcodes[s]); else { sprintf(msg,"Unsupported line speed - %d\n",speed); ermsg(msg); ermsg("Current speed not changed\n"); return(-1); } } /* T T F L U I -- Flush tty input buffer */ ttflui() { long n; if (ttychn == 0) return(-1); /* Not open. */ if (!CHECK_ERR("ttflui: SYS$QIOW", SYS$QIOW(0, ttychn, IO$_READVBLK|IO$M_TIMED|IO$M_PURGE, 0, 0, 0, &n, 0, 0, 0, 0, 0))) perror("flush failed"); #if 0 /* Note: unused */ inbufc = 0; ungotn = -1; #endif return(0); } /* Interrupt Functions */ /* C O N I N T -- Console Interrupt setter */ conint(f) int (*f)(); { /* Set an interrupt trap. */ if (batch) return; /* must ignore signals in bkgrd */ if (conif) return; /* Nothing to do if already on. */ /* check if invoked in background -- if so signals set to be ignored */ if (signal(SIGINT,SIG_IGN) == SIG_IGN) { batch = 1; /* means running in background */ return; } signal(SIGINT,f); /* Function to trap to. */ conif = 1; /* Flag console interrupts on. */ } /* C O N N O I -- Reset console terminal interrupts */ connoi() { /* Console-no-interrupts */ if (batch) return; /* must ignore signals in bkgrd */ signal(SIGINT,SIG_DFL); conif = 0; } /* T T C H K -- Tell how many characters are waiting in tty input buffer */ ttchk() { struct { short count; char first; char reserved1; long reserved2; } t; return (/* inbufc + (ungotn >= 0) + -- 006 Note: unused */ (CHECK_ERR("ttchk: SYS$QIOW", SYS$QIOW(0, ttychn, IO$_SENSEMODE|IO$M_TYPEAHDCNT, 0, 0, 0, &t, sizeof t, 0, 0, 0, 0)) ? t.count : 0)); } /* T T X I N -- Get n characters from tty input buffer */ ttxin(n,buf) int n; char *buf; { int x; long trmmsk[2]; trmmsk[0] = 0; trmmsk[1] = 0; if (!CHECK_ERR("ttxin: SYS$QIOW", SYS$QIOW(0, ttychn, IO$_READVBLK, &ttiosb, 0, 0, buf, n, 0, trmmsk, 0, 0)) || !CHECK_ERR("ttxin: ttiosb.status",ttiosb.status)) return(-1); buf[n] = '\0'; return(ttiosb.size); } /* T T O L -- Similar to "ttinl", but for writing. */ /* * This probably should be buffered with a flush command added. */ ttol(s,n) int n; char *s; { int x; if (ttychn == 0) return(-1); /* Not open. */ if (CHECK_ERR("ttol: SYS$QIOW", SYS$QIOW(0, ttychn, IO$_WRITEVBLK, 0, 0, 0, s, n, 0, 0, 0, 0))) x = 0; else x = -1; 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 */ ttoc(c) char c; { if (ttychn == 0) return(-1); /* Not open. */ if (CHECK_ERR("ttoc: SYS$QIOW", SYS$QIOW(0, ttychn, IO$_WRITEVBLK, 0, 0, 0, &c, 1, 0, 0, 0, 0))) return(0); else 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. Times out & returns error if not completed within "timo" seconds. */ ttinl(dest,max,timo,eol) int max,timo; char *dest; { int x, y, c; int trmmsk[2], func; if (ttychn == 0) return(-1); /* Not open. */ trmmsk[0] = 0; trmmsk[1] = 1 << eol; /* Assumes eol is a control char */ func = IO$_READVBLK; if (timo > 0) func |= IO$M_TIMED; if (!CHECK_ERR("ttinl: SYS$QIOW", SYS$QIOW(0, ttychn, func, &ttiosb, 0, 0, dest, max, timo, trmmsk, 0, 0)) || ttiosb.status == SS$_TIMEOUT /* Check separately so no err msg */ || !CHECK_ERR("ttinl: ttiosb.status",ttiosb.status)) return(-1); return(ttiosb.size); } /* T T I N C -- Read a character from the communication line */ ttinc(timo) int timo; { int n; CHAR ch; int trmmsk[2], func; if (ttychn == 0) return(-1); /* Not open. */ trmmsk[0] = 0; trmmsk[1] = 0; func = IO$_READVBLK; if (timo > 0) func |= IO$M_TIMED; if (!CHECK_ERR("ttinc: SYS$QIOW", SYS$QIOW(0, ttychn, func, &ttiosb, 0, 0, &ch, 1, timo, trmmsk, 0, 0)) || ttiosb.status == SS$_TIMEOUT /* Check separately so no err msg */ || !CHECK_ERR("ttinc: ttiosb.status",ttiosb.status)) return(-1); return(ch & 0377); } /* T T _ C A N C E L -- Cancel i/o on tty channel if not complete */ tt_cancel() { int mask; if (tt_queued) { if (!CHECK_ERR("tt_cancel: SYS$READEF", SYS$READEF(TTY_EFN, &mask))) return(-1); if ((mask & (1<= t;) { if (*cp != '\n' && *cp != '\r') { cp++; *cp++ = '\r'; *cp++ = '\n'; *cp++ = '\0'; break; } } conol(t); } } /* C O N O L L -- Output a string followed by CRLF */ conoll(s) char *s; { conol(s); conol("\r\n"); } /* C O N C H K -- Check if characters available at console */ conchk() { struct { short count; char first; char reserved1; long reserved2; } t; if (batch) return(0); return(CHECK_ERR("conchk: SYS$QIOW", SYS$QIOW(0, conchn, IO$_SENSEMODE|IO$M_TYPEAHDCNT, 0, 0, 0, &t, sizeof t, 0, 0, 0, 0)) ? t.count : 0); } /* C O N I N C -- Get a character from the console */ coninc(timo) int timo; { int n = 0; 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 = conch & 0377; CHECK_ERR("coninc: coniosb.status", coniosb.status); con_queued = 0; } else { ch = -1; vms_status = SS$_TIMEOUT; } } else { func = IO$_READVBLK; if (timo > 0) func |= IO$M_TIMED; CHECK_ERR("coninc: SYS$QIOW", SYS$QIOW(0, conchn, func, 0, 0, 0, &ch, 1, timo, 0, 0, 0)); ch &= 0377; } if (ch == '\r') ch = '\n'; if ((vms_status & 7) == 1) return(ch); else 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 int ch; static int ateof = FALSE; if (ateof) return (EOF); ch = coninc(0); switch (ch) { case ('Y' & 0x1F): case ('C' & 0x1F): ttclos(); /* Close down other terminal */ conres(); /* And cleanup console modes */ exit(SS$_ABORT); /* Fatal exit. */ case ('Z' & 0x1F): ateof = TRUE; return (EOF); default: return (ch); } } /* C O N T T I -- Get character from console or tty, whichever comes */ /* first. 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. */ contti(c, src) int *c, *src; { int mask = 1<