#include "ckcsym.h" #ifdef NOLOCAL char *connv = ""; #else char *connv = "OS-9 Connect Command, 7.0.015, 1 Jan 2000"; /* C K 9 C O N -- Dumb terminal connection to remote system, for OS-9 */ /* Modified from ckucon.c by Bob Larson (blarson@ecla.usc.edu) Edition: 5A(01) by Chris Hemsing, Aachen, W Germany (chris@lfm.rwth-aachen.de): More efficient using sigmask, added character set translation Edition: 5A(02) 07/25/91 Chris Hemsing minor bug fixes, changes for gnu (ansi) C flow control on both sides Edition: 5A(03) 03/04/92 Chris Hemsing Kanji bug fix Edition: 5A(04) 08/20/92 Chris Hemsing flow control bug fix on return from local shell Edition: 5A(05) 10/01/92 Chris Hemsing added sending xon via escape character Edition: 5A(06) 10/09/92 Chris Hemsing escape back to local even on receive burst Edition: 5A(07) 12/22/94 Ulli Schlueter improved overall performance (system load is reduced about 80%, see ck9tio.c), brought it near to the unix version that means: added APC and ansi escape sequence recognition, text session logging, support of SET TERM CR-DISPLAY, support of SET TERM NEWLINE, debug session, keyboard macros and some escape character features Edition: 5A(08) 01/26/95 Ulli Schlueter added network support Edition: 5A(09) 03/13/95 Ulli Schlueter i/o buffers dynamic Edition: 5A(10) 04/19/95 Ulli Schlueter Changed handling of CR from keyboard. Added handling of telnet binary mode. Edition: 5A(11) 04/21/95 Ulli Schlueter Incoming telnet CR/NUL is handled. Made debugging display prettier. Edition: 5A(12) 04/25/95 Ulli Schlueter Keyboard i/o buffered. Edition: 7.0.15 1 Jan 2000 S Rance, F. da Cruz: Adapt to C-Kermit 7.0 and Ultra C. Author: Frank da Cruz Columbia University Center for Computing Activities, January 1985. Copyright (C) 1985, 2000, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #include "ckcdeb.h" #include "ckucmd.h" #include "ckcker.h" #include "ckcasc.h" #ifndef NOCSETS #include "ckcxla.h" /* Character set translation */ #endif /* NOCSETS */ #include "ckcnet.h" #include #include /* Set/Get tty modes */ #ifndef EOS_NOTRDY #ifdef E_NOTRDY #define EOS_NOTRDY E_NOTRDY #endif /* E_NOTRDY */ #endif /* EOS_NOTRDY */ _PROTOTYP( VOID doesc, (char) ); _PROTOTYP( static VOID logchar, (char) ); _PROTOTYP( int hconne, (void) ); extern int local, escape, duplex, parity, flow, seslog, sessft, debses, nopush, mdmtyp, ttnproto, cmask, cmdmsk, network, nettype, deblog, sosi, tnlm, xitsta, what, ttyfd, quiet, backgrd, pflag, tt_crd/*, ttfdflg*/; #ifdef TCPSOCKET extern int tn_nlm, tn_b_nlm, tn_binary; #endif /* TCPSOCKET */ extern long speed; extern char ttname[], sesfil[], myhost[], *ccntab[]; /* from ck9tio.c . . . */ /* console/tty signal flags (incremented by catch) */ extern int csigflg, tsigflg; #ifdef CK_APC extern int apcactive; /* Application Program Command (APC) */ extern int apcstatus; /* items ... */ static int apclength = 0; #ifdef DCMDBUF extern char *apcbuf; #else extern char apcbuf[]; #endif /* DCMDBUF */ static int apcbuflen = APCBUFLEN - 2; #endif /* CK_APC */ /* Character-set items */ #ifndef NOCSETS #ifdef CK_ANSIC /* ANSI C prototypes... */ extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */ extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */ static CHAR (*sxo)(CHAR); /* Local translation functions */ static CHAR (*rxo)(CHAR); /* for output (sending) terminal chars */ static CHAR (*sxi)(CHAR); /* and for input (receiving) terminal chars. */ static CHAR (*rxi)(CHAR); #else /* Not ANSI C... */ extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); /* Character set */ extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); /* translation functions. */ static CHAR (*sxo)(); /* Local translation functions */ static CHAR (*rxo)(); /* for output (sending) terminal chars */ static CHAR (*sxi)(); /* and for input (receiving) terminal chars. */ static CHAR (*rxi)(); #endif /* CK_ANSIC */ extern int language; /* Current language. */ extern struct csinfo fcsinfo[]; /* File character set info. */ extern int tcsr, tcsl; /* Terminal character sets, remote & local. */ #endif /* NOCSETS */ /* We do not need to parse and recognize escape sequences if we are being built without character-set support AND without APC support. */ #ifdef NOCSETS /* No character sets */ #ifndef CK_APC /* No APC */ #ifndef NOESCSEQ #define NOESCSEQ /* So no escape sequence recognizer */ #endif /* NOESCSEQ */ #endif /* CK_APC */ #endif /* NOCSETS */ #ifndef NOSETKEY /* Keyboard mapping */ extern KEY *keymap; /* Single-character key map */ extern MACRO *macrotab; /* Key macro pointer table */ #endif /* NOSETKEY */ #ifdef NOESCSEQ #define chkaes(x, y) #else /* As of edit 178, the CONNECT command will skip past ANSI escape sequences to avoid translating the characters within them. This allows the CONNECT command to work correctly when connected to a remote host that uses a 7-bit ISO 646 national character set, in which characters like '[' would normally be translated into accented characters, ruining the terminal's interpretation (and generation) of escape sequences. As of edit 190, the CONNECT command responds to APC escape sequences (ESC _ text ESC \) if the user SETs TERMINAL APC ON or UNCHECKED, and the program was built with CK_APC defined. Non-ANSI/ISO-compliant escape sequences are not handled. */ /* States for the escape-sequence recognizer. */ #define ES_NORMAL 0 /* Normal, not in an escape sequence */ #define ES_GOTESC 1 /* Current character is ESC */ #define ES_ESCSEQ 2 /* Inside an escape sequence */ #define ES_GOTCSI 3 /* Inside a control sequence */ #define ES_STRING 4 /* Inside DCS,OSC,PM, or APC string */ #define ES_TERMIN 5 /* 1st char of string terminator */ static CHAR escseq; /* 1 = Recognizer is active */ static struct _escinf { char inesc, /* State of sequence recognizer */ oldesc; /* Previous state of recognizer */ } escinf[2]; /* ANSI escape sequence handling. Only the 7-bit form is treated, because translation is not a problem in the 8-bit environment, in which all GL characters are ASCII and no translation takes place. So we don't check for the 8-bit single-character versions of CSI, DCS, OSC, APC, or ST. Here is the ANSI sequence recognizer state table, followed by the code that implements it. Definitions: CAN = Cancel 01/08 Ctrl-X SUB = Substitute 01/10 Ctrl-Z DCS = Device Control Sequence 01/11 05/00 ESC P CSI = Control Sequence Introducer 01/11 05/11 ESC [ ST = String Terminator 01/11 05/12 ESC \ OSC = Operating System Command 01/11 05/13 ESC ] PM = Privacy Message 01/11 05/14 ESC ^ APC = Application Program Command 01/11 05/15 ESC _ ANSI escape sequence recognizer: State Input New State ; Commentary NORMAL (start) ; Start in NORMAL state (any) CAN NORMAL ; ^X cancels (any) SUB NORMAL ; ^Z cancels NORMAL ESC GOTESC ; Begin escape sequence NORMAL other ; NORMAL control or graphic character GOTESC ESC ; Start again GOTESC [ GOTCSI ; CSI GOTESC P STRING ; DCS introducer, consume through ST GOTESC ] STRING ; OSC introducer, consume through ST GOTESC ^ STRING ; PM introducer, consume through ST GOTESC _ STRING ; APC introducer, consume through ST GOTESC 0..~ NORMAL ; 03/00 through 17/14 = Final character GOTESC other ESCSEQ ; Intermediate or ignored control character ESCSEQ ESC GOTESC ; Start again ESCSEQ 0..~ NORMAL ; 03/00 through 17/14 = Final character ESCSEQ other ; Intermediate or ignored control character GOTCSI ESC GOTESC ; Start again GOTCSI @..~ NORMAL ; 04/00 through 17/14 = Final character GOTCSI other ; Intermediate char or ignored control char STRING ESC TERMIN ; Maybe have ST STRING other ; Consume all else TERMIN \ NORMAL ; End of string TERMIN other STRING ; Still in string */ /* chkaes() -- Check ANSI Escape Sequence. Call with EACH character in input stream. Sets global inesc variable according to escape sequence state. Returns 0 normally, 1 if an APC sequence is to be executed. */ int #ifdef CK_ANSIC chkaes(char c, struct _escinf *inf) #else chkaes(c, inf) char c; struct _escinf *inf; #endif /* CK_ANSIC */ /* chkaes */ { inf->oldesc = inf->inesc; /* Remember previous state */ if (c == CAN || c == SUB) /* CAN and SUB cancel any sequence */ inf->inesc = ES_NORMAL; else /* Otherwise */ switch (inf->inesc) { /* enter state switcher */ case ES_NORMAL: /* NORMAL state */ if (c == ESC) /* Got an ESC */ inf->inesc = ES_GOTESC; /* Change state to GOTESC */ break; /* Otherwise stay in NORMAL state */ case ES_GOTESC: /* GOTESC state */ if (c == '[') /* Left bracket after ESC is CSI */ inf->inesc = ES_GOTCSI; /* Change to GOTCSI state */ else if (c == 'P' || (c > 0134 && c < 0140)) { /* P, [, ^, or _ */ inf->inesc = ES_STRING; /* Switch to STRING-absorption state */ #ifdef CK_APC /* APC handled in child only */ if (c == '_' && inf == &escinf[1] && apcstatus != APC_OFF) { debug(F100,"APC begin","",0); apcactive = 1; /* Set APC-Active flag */ apclength = 0; /* and reset APC buffer pointer */ } #endif /* CK_APC */ } else if (c > 057 && c < 0177) /* Final character '0' thru '~' */ inf->inesc = ES_NORMAL; /* Back to normal */ else if (c != ESC) /* ESC in an escape sequence... */ inf->inesc = ES_ESCSEQ; /* starts a new escape sequence */ break; /* Intermediate or ignored ctrl char */ case ES_ESCSEQ: /* ESCSEQ -- in an escape sequence */ if (c > 057 && c < 0177) /* Final character '0' thru '~' */ inf->inesc = ES_NORMAL; /* Return to NORMAL state. */ else if (c == ESC) /* ESC ... */ inf->inesc = ES_GOTESC; /* starts a new escape sequence */ break; /* Intermediate or ignored ctrl char */ case ES_GOTCSI: /* GOTCSI -- In a control sequence */ if (c > 077 && c < 0177) /* Final character '@' thru '~' */ inf->inesc = ES_NORMAL; /* Return to NORMAL. */ else if (c == ESC) /* ESC ... */ inf->inesc = ES_GOTESC; /* starts over. */ break; /* Intermediate or ignored ctrl char */ case ES_STRING: /* Inside a string */ if (c == ESC) /* ESC may be 1st char of terminator */ inf->inesc = ES_TERMIN; /* Go see. */ #ifdef CK_APC else if (apcactive && (apclength < apcbuflen)) /* If in APC, */ apcbuf[apclength++] = c; /* deposit this character. */ else { /* Buffer overrun */ apcactive = 0; /* Discard what we got */ apclength = 0; /* and go back to normal. */ apcbuf[0] = 0; /* Not pretty, but what else */ inf->inesc = ES_NORMAL; /* can we do? (ST might not come) */ } #endif /* CK_APC */ break; /* Absorb all other characters. */ case ES_TERMIN: /* May have a string terminator */ if (c == '\\') { /* which must be backslash */ inf->inesc = ES_NORMAL; /* If so, back to NORMAL */ #ifdef CK_APC if (apcactive) { /* If it was an APC string, */ debug(F101,"APC terminated","",c); apcbuf[apclength] = NUL; /* terminate it and then ... */ return(1); } #endif /* CK_APC */ } else { /* Otherwise */ inf->inesc = ES_STRING; /* Back to string absorption. */ #ifdef CK_APC if (apcactive && (apclength+1 < apcbuflen)) { /* In APC string */ apcbuf[apclength++] = ESC; /* deposit the Esc character */ apcbuf[apclength++] = c; /* and this character too */ } #endif /* CK_APC */ } } return(0); } #endif /* NOESCSEQ */ static CHAR active, /* Variables global to this module */ quitnow, #ifdef NETCONN telnet_cnct, /* 1 if telnet connection */ #endif /* NETCONN */ #ifdef TNCODE c_ttprv, /* Previous character from comm line */ #endif /* TNCODE */ c_slprv, /* Previous session log character */ inshift, outshift, /* SO/SI shift state */ dohangup; /* 2 = user requested hangup, 1 = hangup due to error */ #define LIBUFL 200 /* Line buffer */ #define LOBUFL (2*LIBUFL) /* for cr/lf display */ #ifdef DYNAMIC static CHAR *libuf; static CHAR *lobuf; #else static CHAR libuf[LIBUFL]; static CHAR lobuf[LOBUFL]; #endif /* DYNAMIC */ static int ibc; static CHAR *ibp; #define KIBUFL 80 #define KOBUFL (3*KIBUFL) #ifdef DYNAMIC static CHAR *kibuf, *kobuf; #else static CHAR kibuf[KIBUFL]; static CHAR kobuf[KOBUFL]; #endif /* DYNAMIC */ static CHAR *kibp, *kobp; static int kibc, kobc; static struct sgbuf opt; /* sgtty info... */ /* C K C G E T C -- C-Kermit CONNECT Get Character */ /* Semi buffered read from communication device. On error, returns ttinc's return code (see ttinc() description). Dummy argument for compatible calling conventions with ttinc(). NOTE: We don't have a macro for this because we have to pass a pointer to this function as an argument to tn_doop(). */ int ckcgetc(dummy) int dummy; { if (ibc > 0) { ibc -= 1; return *ibp++; } else return ttinc(0); } /* K B G E T C -- Get one character from keyboard (semi buffered). */ static int kbgetc () { if (kibc > 0) { kibc -= 1; return *kibp++; } return coninc(0); } /* K B P U T C -- Send one character from keyboard (buffered) */ /* Using write() and not ttol() because less overhead. When an error occurrs, ttol() will call ttclos() on network connections where as ttoc(), which was used for output in the unbuffered version, will not (ckutio.c: why that?). The first arg of ttol() schould (must) be a null terminated string!? */ static int kbputc (c) int c; { if (c < 0 || kobc >= KOBUFL) { if (write(ttyfd, kobuf, kobc) < 0) { debug(F101, "conect kbputc errno", NULL, errno); return -1; } kobp = kobuf; kobc = 0; } if (!(c < 0)) { *kobp++ = dopar(c); kobc += 1; } return 0; } /* C O N E C T -- Perform terminal connection */ conect() { register int c; /* c is a character, but must be signed integer to pass thru -1, which is the modem disconnection signal, and is different from the character 0377 */ register int csave; /* Another copy of c */ register int n; char temp[80]; int we_have_data; #ifndef NOESCSEQ int apcrc = 0; /* APC recognized flag */ #endif /* NOESCSEQ */ #ifndef NOCSETS int langsv; /* For remembering language setting. */ int tcs; /* Intermediate ("transfer") character set. */ #endif /* NOCSETS */ #ifndef NOSETKEY MACRO kmptr = NULL; /* Pointer to current key macro */ #endif /* NOSETKEY */ if (!local) { #ifdef NETCONN printf("Sorry, you must SET LINE or SET HOST first\n"); #else printf("Sorry, you must SET LINE first\n"); #endif /* !NETCONN */ return(0); } if (speed < 0 && network == 0 /*&& ttfdflg == 0*/) { printf("Sorry, you must SET SPEED first\n"); return(0); } #ifdef COMMENT /* This is handled by SET ESCAPE */ if ((escape < 0) || (escape > 0177)) { printf("Your escape character is not ASCII - %d\n",escape); return(0); } #endif /* COMMENT */ #ifdef DYNAMIC if (libuf == NULL) { /* Allocate input/output buffers */ n = LIBUFL + LOBUFL + KIBUFL + KOBUFL; if ((libuf = (CHAR *)malloc(n)) == NULL) { printf("Sorry, CONNECT i/o buffers can't be allocated\n"); return 0; } lobuf = libuf + LIBUFL; kibuf = lobuf + LOBUFL; kobuf = kibuf + KIBUFL; } #endif /* DYNAMIC */ if (ttopen(ttname,&local, #ifdef NETCONN network ? -nettype : mdmtyp, #else mdmtyp, #endif /* NETCONN */ 0) < 0) { sprintf(temp,"Sorry, can't open %s",ttname); perror(temp); return(0); } dohangup = 0; /* Hangup not requested yet */ if (!quiet #ifdef CK_APC && !apcactive #endif /* CK_APC */ ) { #ifdef NETCONN printf("Connecting %s %s", network ? "to host" : "through", ttname); if (!network && speed >= 0L) printf(", speed %d", speed); #else printf("Connecting thru %s, speed %d",ttname,speed); #endif /* NETCONN */ printf(".\nThe escape character is Ctrl-%c (ASCII %d, %s)\n", ctl(escape), escape, escape == 127 ? "DEL" : ccntab[escape]); printf("Type the escape character followed by C to get back,\n"); printf("or followed by ? to see other options.\n"); if (seslog) printf("(Session logged to %s, %s)\n",sesfil, sessft ? "binary" : "text"); if (debses) printf("(Debugging Display...)\n"); } /* Condition console terminal and communication line */ if (conbin(escape) < 0) { printf("Sorry, can't condition console terminal\n"); return(0); } if (flow==1) { /*if xon/xoff you should have it running on both*/ if (_gs_opt(0,&opt) <0) /* Structure for restoring */ { printf("Sorry, can't condition console terminal\n"); return(0); } opt.sg_xon = 0x11; opt.sg_xoff = 0x13; _ss_opt(0,&opt); _ss_opt(1,&opt); /* set new modes . */ } if (ttvt(speed,flow) < 0) { conres(); printf("Sorry, Can't condition communication line\n"); return(0); } #ifdef NETCONN telnet_cnct = network && ttnproto == NP_TELNET; #endif /* NETCONN */ #ifndef NOCSETS /* Set up character set translations */ tcs = gettcs(tcsr, tcsl); /* Get intermediate set */ if (tcsr == tcsl) { /* Remote and local sets the same? */ sxo = rxo = NULL; /* If so, no translation. */ sxi = rxi = NULL; } else { /* Otherwise, set up */ sxo = xls[tcs][tcsl]; /* translation function */ rxo = xlr[tcs][tcsr]; /* pointers for output functions */ sxi = xls[tcs][tcsr]; /* and for input functions. */ rxi = xlr[tcs][tcsl]; } /* This is to prevent use of zmstuff() and zdstuff() by translation functions. They only work with disk i/o, not with communication i/o. Luckily Russian translation functions don't do any stuffing... */ langsv = language; #ifndef NOCYRIL if (language != L_RUSSIAN) #endif /* NOCYRIL */ language = L_USASCII; #ifndef NOESCSEQ /* We need to activate the escape-sequence recognition feature when: (a) translation is elected, AND (b) the local and/or remote set is a 7-bit set other than US ASCII. Or: SET TERMINAL APC is not OFF (handled in the next statement). */ escseq = (tcs != TC_TRANSP) && /* Not transparent */ (fcsinfo[tcsl].size == 128 || fcsinfo[tcsr].size == 128) && /* 7 bits */ (fcsinfo[tcsl].code != FC_USASCII); /* But not ASCII */ #endif /* NOESCSEQ */ #endif /* NOCSETS */ #ifndef NOESCSEQ #ifdef CK_APC escseq = escseq || (apcstatus != APC_OFF); apcactive = 0; /* An APC command is not active */ apclength = 0; /* ... */ #endif /* CK_APC */ escinf[0].oldesc = escinf[1].oldesc = -1; escinf[0].inesc = escinf[1].inesc = ES_NORMAL; /* Initial state of recognizer */ debug(F101,"escseq","",escseq); #endif /* NOESCSEQ */ inshift = outshift = 0; /* Initial shift state. */ /* Main loop. The treatment of the 8th bit of keyboard characters is governed by SET COMMAND BYTESIZE (cmdmsk). The treatment of the 8th bit of characters sent to the remote is governed by SET TERMINAL BYTESIZE (cmask). This distinction was introduced in edit C-Kermit 5A(164). */ what = W_CONNECT; /* Keep track of what we're doing */ active = 1; csigflg = tsigflg = 1; /* force signal setup */ while (active) { /* Increment signal mask, signals must be masked between _ss_ssig and sleep */ if (ibc <= 0) { sigmask(1); if (csigflg) { csigflg = 0; _ss_ssig(0, SIGARB); } #ifdef NETCONN if (!network) { #endif /* NETCONN */ if (tsigflg) { tsigflg = 0; _ss_ssig(ttyfd, SIGARB+1); } sleep(0); /* signal mask is cleared */ #ifdef NETCONN } else { extern int ttevid; /* Note: We're going to wait for an event with the signal mask set. That's ok, because only then the event wait routine recognizes signals that arrived between the above call of sigmask and the entry of event wait! The effect is that an arriving signal will put us in the active queue, but the intercept routine is not executed. That is ok too, because after the execution of the below sigmask call the signal mask is clear and the os9call exception handler checks for pending signals - then the intercept routine is executed. */ _ev_wait(ttevid, 1, 0x7fff); sigmask(0); } #endif /* NETCONN */ } /* Avoid this expensive system call, cause it's unnecessary: we're calling read only if there's data available, then no signal is pending anymore. */ #ifdef COMMENT _ss_rel(0); _ss_rel(ttyfd); #endif /* COMMENT */ /* Loop while data on keybord or remote port. Do check console to get characters through even if communication line receives a burst. */ do { we_have_data = 0; if ((n = _gs_rdy(0)) > 0 /* Using read because missing conxin() - see the discussion of ckucon.c:kbget(). */ ? (n = read(0, kibp = kibuf, n > KIBUFL ? KIBUFL : n)) > 0 : (n = errno != EOS_NOTRDY ? -1 : 0, 0)) { /* Wait some arbitrary time for buffer fill to reduce system load and to avoid sending small packets over the network. */ if (n < 10) tsleep(2); we_have_data = 1; kibc = n; /* kobc = 0; */ kobp = kobuf; do { /* while (kibc || kmptr) */ #ifndef NOSETKEY if (kmptr) { if ((c = (CHAR)*kmptr++) == NUL) { kmptr = NULL; continue; /* get next character from kb or break */ } } else #endif /* NOSETKEY */ c = kbgetc(); /* Get character from keyboard */ c &= cmdmsk; #ifndef NOSETKEY /* Note: kmptr is NULL if we got character c from the keyboard, and it is not NULL if it came from a macro. In the latter case, we must avoid expanding it again. */ if (!kmptr && macrotab[c]) { /* Macro definition for c? */ kmptr = macrotab[c]; /* Yes, set up macro pointer */ continue; /* and restart the loop */ } else c = keymap[c]; /* else use single-char keymap */ #endif /* NOSETKEY */ if ( #ifndef NOSETKEY !kmptr && #endif /* NOSETKEY */ (c & 0177) == escape) { /* Look for escape char */ if (kobc) kbputc(-1); /* Flush kb output buffer */ c = kbgetc() & 0177; /* Got esc, get its arg */ doesc(c); /* And process it */ if (!active) break; /* immediately leave we_have_data loop to avoid burst receive */ } else { /* Ordinary character */ csave = c; /* Save it before translation for local echo */ #ifndef NOCSETS #ifndef NOESCSEQ if (escinf[0].inesc == ES_NORMAL) #endif /* NOESCSEQ */ { /* Translate character sets */ if (sxo) c = (*sxo)(c); /* Local to intermediate. */ if (rxo) c = (*rxo)(c); /* Intermediate to remote. */ } if (escseq) chkaes((char)c, &escinf[0]); #endif /* NOCSETS */ /* If Shift-In/Shift-Out selected and we have a 7-bit connection, handle shifting here. */ if (sosi) { /* Shift-In/Out selected? */ if (cmask == 0177) { /* In 7-bit environment? */ if (c & 0200) { /* 8-bit character? */ if (outshift == 0) { /* If not shifted, */ kbputc(SO); /* shift. */ outshift = 1; } } else { if (outshift == 1) { /* 7-bit character */ kbputc(SI); /* If shifted, */ outshift = 0; /* unshift. */ } } } if (c == SO) outshift = 1; /* User typed SO */ if (c == SI) outshift = 0; /* User typed SI */ } c &= cmask; /* Apply Kermit-to-host mask now. */ kbputc(c); if (c == CR) { /* Carriage Return */ #ifdef TNCODE /* Handle TELNET NEWLINE ON/OFF/RAW */ if (telnet_cnct) { switch (!TELOPT_ME(TELOPT_BINARY) ? tn_nlm : tn_b_nlm) { case TNL_CRLF: /* Send CR/LF */ kbputc(LF); break; case TNL_CRNUL: /* Send CR/NUL */ kbputc(NUL); break; } } #endif /* TNCODE */ if (tnlm) /* TERMINAL NEWLINE ON */ kbputc(LF); /* Send CR/LF */ } #ifdef TNCODE /* If user types the 0xff character (TELNET IAC), it must be doubled. */ else /* Not CR */ if ((dopar((CHAR) c) == IAC) && /* IAC (0xff) */ telnet_cnct) { kbputc(IAC); /* double it */ } #endif /* TNCODE */ while (1) { /* Handle local echo */ if (duplex) { /* Half duplex? */ if (debses) { conol(dbchr(csave)); /* the original char */ if (csave == LF) conoll(""); } else conoc(csave); /* Yes, also echo it. */ if (seslog) /* And maybe log it. */ logchar( /* sessft == 0 && csave == '\l' ? '\n' : */ csave ); if (csave == CR && tt_crd) { /* TERM CR-DISPLAY */ csave = LF; continue; /* Output LF and leave loop */ } } break; /* Leave pseudo loop */ } } /* end if ((c & 0177) == escape) */ } while (kibc #ifndef NOSETKEY || kmptr #endif /* NOSETKEY */ ); if (kobc) { /* Flush kb output buffer */ if (kbputc(-1) < 0) { perror("\r\012Can't send character\012"); active = 0; } } } /* end if data on console */ else if (n < 0) { debug(F101,"conect console errno", NULL, errno); active = 0; } if (active == 0) break; /* Leave we-have-data loop */ /* Get characters from the communication line and put to the console. */ if ((n = ibc) > 0 || ((n = ttchk()) > 0 ? (n = ttxin(n > LIBUFL ? LIBUFL : n, ibp = libuf)) > 0 : (n = errno != EOS_NOTRDY ? -1 : 0, 0))) { register CHAR *pi = ibp; register CHAR *po = lobuf; we_have_data = 1; do { c = *pi++; #ifdef TNCODE if (c == NUL && telnet_cnct && !TELOPT_U(TELOPT_BINARY) && c_ttprv == CR) { c_ttprv = c; continue; /* get next character */ } c_ttprv = c; /* Handle TELNET negotiations... */ if (c == IAC && telnet_cnct) { register int tx; debug(F100,"CONNECT got IAC","",0); if (po - lobuf > 0) { /* Dump screen-output buffer */ conxo(po - lobuf, (char *)lobuf); po = lobuf; } ibc = n - 1; /* save for ckcgetc */ ibp = pi; tx = tn_doop((CHAR)(c & 0xff),duplex,ckcgetc); n = ibc + 1; pi = ibp; if ((tx) == 0) { continue; /* get next character */ } else if (tx == -1) { /* I/O error */ if (!quiet) printf("\n\012Communications disconnect "); #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ ttclos(0); /* close network connection */ dohangup = 1; active = 0; break; /* Leave comm-data loop */ /* NOTREACHED */ } else if ((tx == 1) && (!duplex)) { /* ECHO change */ duplex = 1; /* Turn on local echo */ debug(F101,"CONNECT TELNET duplex change","",duplex); continue; /* get next character */ } else if ((tx == 2) && (duplex)) { /* ECHO change */ duplex = 0; debug(F101,"CONNECT TELNET duplex change","",duplex); continue; /* get next character */ } else if (tx == 3) { /* Quoted IAC */ c = IAC; } else continue; /* Negotiation OK, get next char. */ } #endif /* TNCODE */ if (debses) { while (1) { conol(dbchr(c)); if (c == LF) conoll(""); if (seslog) logchar(c); if (c == CR && tt_crd) { /* TERM CR-DISPLAY */ c = LF; continue; /* Output LF and leave loop */ } break; /* Leave pseudo loop */ } } else { c &= cmask; if (sosi) { if (c == SO) { inshift = 1; continue; /* get next character */ } else if (c == SI) { inshift = 0; continue; /* get next character */ } if (inshift) c |= 0200; } #ifndef NOCSETS #ifndef NOESCSEQ /* If not in an esc sequ. */ if (escinf[1].inesc == ES_NORMAL) #endif /* NOESCSEQ */ { /* Translate character sets */ if (sxi) c = (*sxi)(c); if (rxi) c = (*rxi)(c); } #endif /* NOCSETS */ #ifndef NOESCSEQ if (escseq) /* If handling escape sequences */ /* update our state */ apcrc = chkaes((char)c, &escinf[1]); #ifdef CK_APC /* If we are handling APCs, we have several possibilities at this point: 1. Ordinary character to be written to the screen. 2. An Esc; we can't write it because it might be the beginning of an APC. 3. The character following an Esc, in which case we write Esc, then char, but only if we have not just entered an APC sequence. */ if (escseq && apcstatus != APC_OFF) { if (escinf[1].inesc == ES_GOTESC) /* Don't write ESC yet */ continue; /* Handle next character */ else if (escinf[1].oldesc == ES_GOTESC && !apcactive) { *po++ = ESC; /* Write saved ESC */ if (seslog) logchar((char)ESC); } else if (apcrc) { /* We have an APC */ debug(F111,"APC complete",apcbuf,apclength); active = 0; n--; /* uncount character */ break; /* Force screen update */ } } #endif /* CK_APC */ #endif /* NOESCSEQ */ #ifdef CK_APC if (!apcactive) /* Ignore when in APC sequence */ #endif /* CK_APC */ { c &= cmdmsk; /* Apply command mask. */ if (c == CR && tt_crd) { /* SET TER CR-DISP CRLF */ *po++ = c; if (seslog) logchar(c); c = LF; } *po++ = c; } if (seslog) logchar(c); /* Handle session log */ } /* end no debug session */ } while (--n > 0); ibc = n; /* save remaining input count */ ibp = pi; /* save input buffer position */ if (po - lobuf > 0) conxo(po - lobuf,(char *)lobuf); /* Output whole buffer */ } /* end data on communications line */ else if (n < 0) { debug(F101, "conect comm errno", NULL, errno); if (!quiet) printf("\n\lCommunications disconnect "); if (network) ttclos(0); dohangup = 1; active = 0; } } while (we_have_data && active); /* end while(we_have_data) */ } /* end while(active) */ _ss_rel(0); if (!network) _ss_rel(ttyfd); conres(); /* Reset the console. */ if (dohangup > 0) { /* If hangup requested, do that. */ #ifndef NODIAL if (dohangup > 1) /* User asked for it */ if (mdmhup() < 1) /* Maybe hang up via modem */ #endif /* NODIAL */ tthang(); /* And make sure we don't hang up */ dohangup = 0; /* again unless requested again. */ } if (quitnow) doexit(GOOD_EXIT, xitsta); /* Exit now if requested. */ if (!quiet #ifdef CK_APC && !apcactive #endif /* CK_APC */ ) printf("[Back at %s]", *myhost ? myhost : "local system"); #ifdef CK_APC if (!apcactive) #endif /* CK_APC */ printf("\n"); what = W_NOTHING; /* So console modes are set right. */ #ifndef NOCSETS language = langsv; /* Restore language */ #endif /* NOCSETS */ return(1); } /* H C O N N E -- Give help message for connect. */ int #ifdef CK_ANSIC hconne(void) #else hconne() #endif /* CK_ANSIC */ { int c; static char *hlpmsg[] = {"\ \r\012C to close the connection, or:\r", " 0 (zero) to send a null\r", " B to send a BREAK\r", #ifdef NETCONN " I to send a network interrupt packet\r", #ifdef TCPSOCKET " A to send Are You There?\r", #endif /* TCPSOCKET */ #endif /* NETCONN */ " U to hangup and close connection\r", " Q to hangup and quit Kermit\r", " S for status\r", " X to send an XON\r", #ifndef NOPUSH " ! to push to local shell\r", #endif /* NOPUSH */ " ? for help\r", " \\ backslash code:\r", " \\nnn decimal character code\r", " \\Onnn octal character code\r", " \\Xhh hexadecimal character code\r", " terminate with carriage return.\r", " escape character twice to send the escape character.\r\012\r\012\r", ""}; conola(hlpmsg); /* Print the help message. */ conol("Command>"); /* Prompt for command. */ c = kbgetc() & 0177; /* Get character, strip any parity. */ conoc(c); /* Echo it. */ if (c != CMDQ) conoll(""); return(c); /* Return it. */ } /* D O E S C -- Process an escape character argument */ VOID #ifdef CK_ANSIC doesc(char c) #else doesc(c) char c; #endif /* CK_ANSIC */ { CHAR d; char temp[80]; while (1) { if (c == escape) { /* Send escape character */ d = dopar(c); ttoc(d); return; } else /* Or else look it up below. */ if (isupper(c)) c = tolower(c); switch (c) { #ifdef NETCONN case 'i': /* Send Interrupt */ case '\011': #ifdef TCPSOCKET #ifndef IP #define IP 244 #endif /* IP */ if (telnet_cnct) { /* TELNET */ temp[0] = (CHAR) IAC; /* I Am a Command */ temp[1] = (CHAR) IP; /* Interrupt Process */ temp[2] = NUL; /* For ttol debug call */ ttol((CHAR *)temp,2); } else #endif /* TCPSOCKET */ conoc(BEL); return; #ifdef TCPSOCKET case 'a': /* "Are You There?" */ case '\01': #ifndef AYT #define AYT 246 #endif /* AYT */ if (telnet_cnct) { temp[0] = (CHAR) IAC; /* I Am a Command */ temp[1] = (CHAR) AYT; /* Are You There? */ temp[2] = NUL; /* For ttol debug call */ ttol((CHAR *)temp,2); } else conoc(BEL); return; #endif /* TCPSOCKET */ #endif /* NETCONN */ case 'c': /* Close connection */ case '\03': active = 0; conol("\r\012"); return; case 'b': /* Send a BREAK signal */ case '\02': if (ttsndb()<0) conol("\r\nCan't send break\r\012"); return; case 'u': /* Hangup */ case '\010': dohangup = 2; active = 0; conol("\r\012"); return; case 'q': dohangup = 2; quitnow = 1; active = 0; conol("\r\012"); return; case '!': #ifndef NOPUSH if (!nopush) { conres(); zshcmd(""); if (conbin(escape) < 0) { printf("Error returning to remote session\n"); active = 0; } /*if xon/xoff you should have it running on both*/ if (flow == 1) { if ((_ss_opt(0,&opt) < 0)||(_ss_opt(1,&opt) < 0)) { printf("Error returning to remote session\n"); active = 0; } } } else conoc(BEL); #else conoc(BEL); #endif /* NOPUSH */ return; case 's': /* Status */ sprintf(temp, "\015\012Connected %s %s", network ? "to" : "through", ttname); conol(temp); if (speed >= 0L) { sprintf(temp,", speed %ld", speed); conoll(temp); } else conoll(""); sprintf(temp, "Terminal bytesize: %d, Command bytesize: %d, Parity: ", (cmask == 0177) ? 7 : 8, (cmdmsk == 0177) ? 7 : 8 ); conol(temp); switch (parity) { case 0: conoll("none"); break; case 'e': conoll("even"); break; case 'o': conoll("odd"); break; case 's': conoll("space"); break; case 'm': conoll("mark"); break; } sprintf(temp,"Terminal echo: %s", duplex ? "local" : "remote"); conoll(temp); if (seslog) { conol("Logging to: "); conoll(sesfil); } if (!network) shomdm(); #ifdef OSK conoll(""); #endif /* OSK */ return; case 'h': /* Help */ case '?': /* Help */ c = hconne(); continue; case 'x': /* send xon */ ttoc(dopar(XON)); return; case '0': /* Send a null */ c = '\0'; d = dopar(c); ttoc(d); return; case SP: /* Space, ignore */ return; default: /* Other */ if (c == CMDQ) { /* Backslash escape */ int x = 1; char *ecbp = temp; *ecbp++ = c; while (((c = (kbgetc() & cmdmsk)) != '\012') && (c != '\n') && ++x < sizeof temp) *ecbp++ = c; *ecbp = NUL; ecbp = temp; do { x = xxesc(&ecbp); /* Interpret it */ if (x >= 0) { /* No key mapping here */ ttoc(dopar(x)); } else { /* Invalid backslash code. */ conoc(BEL); break; } } while (*ecbp != NUL); return; } conoc(BEL); return; /* Invalid esc arg, beep */ } } } static VOID #ifdef CK_ANSIC logchar(char c) #else logchar(c) char c; #endif /* CK_ANSIC */ /* logchar */ { /* Log character c to session log */ /*if (seslog) */ if ((sessft != 0) || ((c == '\012' || c == '\n') ? c_slprv != '\n' : c != '\0' && c != XON && c != XOFF)) if (zchout(ZSFILE,c) < 0) { conoll(""); conoll("ERROR WRITING SESSION LOG, LOG CLOSED!"); seslog = 0; } c_slprv = c; } #endif /* !NOLOCAL */