2 char *connv = "CONNECT Command for UNIX:select(), 8.0.135, 29 Nov 2002";
4 /* C K U C N S -- Terminal connection to remote system, for UNIX */
6 Author: Frank da Cruz <fdc@columbia.edu>,
7 Columbia University Academic Information Systems, New York City.
9 Copyright (C) 1985, 2004,
10 Trustees of Columbia University in the City of New York.
11 All rights reserved. See the C-Kermit COPYING.TXT file or the
12 copyright text in the ckcmai.c module for disclaimer and permissions.
16 This version of the UNIX CONNECT module uses select(), which is required for
17 Kerberos encryption. Thus it can be used only on UNIX systems that support
18 select() on both TCP/IP and serial connections. A separate module that uses
19 a completely portable fork() structure can be used on systems where select()
20 is not available or does not work as required.
23 #include "ckcdeb.h" /* Common things first */
31 #endif /* _NO_PROTO */
35 #include <errno.h> /* Error numbers */
38 #include <time.h> /* For FD_blah */
39 #ifdef SYSTIMEH /* (IRIX 5.3) */
44 #ifdef BSD42HACK /* Why is this necessary? */
47 #endif /* DCLTIMEVAL */
48 #endif /* BSD42HACK */
50 /* Kermit-specific includes */
52 #include "ckcasc.h" /* ASCII characters */
53 #include "ckcker.h" /* Kermit things */
54 #include "ckucmd.h" /* For xxesc() prototype */
55 #include "ckcnet.h" /* Network symbols */
57 #include "ckcxla.h" /* Character set translation */
61 #include <kernel/OS.h>
66 #include <signal.h> /* Signals */
68 /* All the following is for select()... */
70 #ifdef CKTIDLE /* Timeouts only for SET TERM IDLE */
78 #endif /* DCLTIMEVAL */
80 #ifdef DCLTIMEVAL /* Declare timeval ourselves */
85 #else /* !DCLTIMEVAL */
88 #include <sys/timeb.h>
89 #endif /* SYSTIMEBH */
90 #endif /* NOSYSTIMEBH */
91 #endif /* DCLTIMEVAL */
96 #include <sys/select.h>
98 #endif /* SCO_OSR504 */
102 #define FD_SETSIZE 256
104 #define FD_SETSIZE 32
105 #endif /* CK_FORWARD_X */
106 #endif /* FD_SETSIZE */
111 /* The three interior args to select() are (int *) rather than (fd_set *) */
114 #endif /* INTSELECT */
115 #endif /* HPUX1100 */
119 /* Internal function prototypes */
123 _PROTOTYP( VOID ttflux, (void) );
124 _PROTOTYP( VOID doesc, (char) );
125 _PROTOTYP( int hconne, (void) );
127 _PROTOTYP( VOID shomdm, (void) );
129 _PROTOTYP( static int kbget, (void) );
130 _PROTOTYP( static int ckcputf, (void) );
132 /* External variables */
134 extern struct ck_p ptab[];
136 extern int local, escape, duplex, parity, flow, seslog, sessft, debses,
137 mdmtyp, ttnproto, cmask, cmdmsk, network, nettype, sosi, tnlm,
138 xitsta, what, ttyfd, ttpipe, quiet, backgrd, pflag, tt_crd, tn_nlm, ttfdflg,
139 tt_escape, justone, carrier, ttpty, hwparity;
142 extern int dialmhu, dialsta;
146 extern FILE * learnfp;
148 static ULONG learnt1;
149 static char learnbuf[LEARNBUFSIZ] = { NUL, NUL };
150 static int learnbc = 0;
151 static int learnbp = 0;
152 static int learnst = 0;
156 extern char ttname[], sesfil[], myhost[], *ccntab[];
158 extern int tn_b_nlm, tn_rem_echo;
162 extern char * tt_trigger[], * triggerval;
163 #endif /* CK_TRIGGER */
166 extern int tt_idlelimit, tt_idleact;
167 extern char * tt_idlestr;
168 static int idlelimit = 0;
170 extern int cx_status; /* CONNECT status code */
175 extern int apcactive; /* Application Program Command (APC) */
176 extern int apcstatus; /* items ... */
177 static int apclength = 0;
181 extern char apcbuf[];
183 static int apcbuflen = APCBUFLEN - 2;
187 extern int autodl; /* Auto download */
193 extern int kstartactive;
194 #endif /* CK_AUTODL */
198 #endif /* CK_ENCRYPTION */
202 static int zmdlok = 1; /* Zmodem autodownloads available */
204 static int zmdlok = 0; /* Depends on external protocol def */
205 #endif /* XYZ_INTERNAL */
207 static int zmdlok = 0; /* Not available at all */
210 #ifndef NOSETKEY /* Keyboard mapping */
211 extern KEY *keymap; /* Single-character key map */
212 extern MACRO *macrotab; /* Key macro pointer table */
213 static MACRO kmptr = NULL; /* Pointer to current key macro */
214 #endif /* NOSETKEY */
216 /* Global variables local to this module */
220 quitnow = 0, /* <esc-char>Q was typed */
221 dohangup = 0, /* <esc-char>H was typed */
222 inshift = 0, /* SO/SI shift states */
225 static char ecbuf[10], *ecbp; /* Escape char buffer & pointer */
228 #define IBUFL 1536 /* Input buffer length */
231 #endif /* CK_SMALL */
233 static int obc = 0; /* Output buffer count */
236 #define OBUFL 1024 /* Output buffer length */
242 #define TMPLEN 4096 /* Temporary message buffer length */
245 #endif /* BIGBUFOK */
248 static char *ibuf = NULL, *obuf = NULL, *temp = NULL; /* Buffers */
250 static char ibuf[IBUFL], obuf[OBUFL], temp[TMPLEN];
254 static char tnopt[4];
258 static char *ibp; /* Input buffer pointer */
260 static char *ibp = ibuf; /* Input buffer pointer */
262 static int ibc = 0; /* Input buffer count */
265 static char *obp; /* Output buffer pointer */
267 static char *obp = obuf; /* Output buffer pointer */
270 /* Character-set items */
272 static int unicode = 0;
275 #ifdef CK_ANSIC /* ANSI C prototypes... */
276 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */
277 extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */
278 static CHAR (*sxo)(CHAR); /* Local translation functions */
279 static CHAR (*rxo)(CHAR); /* for output (sending) terminal chars */
280 static CHAR (*sxi)(CHAR); /* and for input (receiving) terminal chars. */
281 static CHAR (*rxi)(CHAR);
282 #else /* Not ANSI C... */
283 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); /* Character set */
284 extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); /* translation functions. */
285 static CHAR (*sxo)(); /* Local translation functions */
286 static CHAR (*rxo)(); /* for output (sending) terminal chars */
287 static CHAR (*sxi)(); /* and for input (receiving) terminal chars. */
288 static CHAR (*rxi)();
289 #endif /* CK_ANSIC */
290 extern int language; /* Current language. */
291 static int langsv; /* For remembering language setting. */
292 extern struct csinfo fcsinfo[]; /* File character set info. */
293 extern int tcsr, tcsl; /* Terminal character sets, remote & local. */
294 static int tcs; /* Intermediate ("transfer") character set. */
295 static int tcssize = 0; /* Size of tcs */
296 #ifdef UNICODE /* UTF-8 support */
298 extern int (*xl_ufc[MAXFCSETS+1])(USHORT); /* Unicode to FCS */
299 extern USHORT (*xl_fcu[MAXFCSETS+1])(CHAR); /* FCS to Unicode */
300 extern int (*xuf)(USHORT); /* Translation function UCS to FCS */
301 extern USHORT (*xfu)(CHAR); /* Translation function FCS to UCS */
303 extern int (*xl_ufc[MAXFCSETS+1])();
304 extern USHORT (*xl_fcu[MAXFCSETS+1])();
306 extern USHORT (*xfu)();
307 #endif /* CK_ANSIC */
311 static int printing = 0;
314 We do not need to parse and recognize escape sequences if we are being built
315 without character-set support AND without APC support.
322 #else /* NOESCSEQ not defined from outside */
324 #ifdef NOCSETS /* No character sets */
325 #ifndef CK_APC /* No APC */
326 #ifndef XPRINT /* No transparent printing */
327 #define NOESCSEQ /* So no escape sequence recognizer */
331 #endif /* NOESCSEQ */
333 static int escseq = 0; /* 1 = Recognizer is active */
334 static int inesc[2] = { 0, 0 }; /* State of sequence recognizer */
335 static int oldesc[2] = { -1, -1 }; /* Previous state of recognizer */
338 #define chkaes(x,y) 0
341 As of C-Kermit 5A(178), the CONNECT command skips past ANSI escape sequences
342 to avoid translating the characters within them. This allows the CONNECT
343 command to work correctly with a host that uses a 7-bit ISO 646 national
344 character set, in which characters like '[' would normally be translated
345 into accented characters, ruining the terminal's interpretation (and
346 generation) of escape sequences.
348 As of 5A(190), the CONNECT command responds to APC escape sequences
349 (ESC _ text ESC \) if the user SETs TERMINAL APC ON or UNCHECKED, and the
350 program was built with CK_APC defined.
352 Non-ANSI/ISO-compliant escape sequences are not handled. */
354 /* States for the escape-sequence recognizer. */
356 #define ES_NORMAL 0 /* Normal, not in an escape sequence */
357 #define ES_GOTESC 1 /* Current character is ESC */
358 #define ES_ESCSEQ 2 /* Inside an escape sequence */
359 #define ES_GOTCSI 3 /* Inside a control sequence */
360 #define ES_STRING 4 /* Inside DCS,OSC,PM, or APC string */
361 #define ES_TERMIN 5 /* 1st char of string terminator */
364 ANSI escape sequence handling. Only the 7-bit form is treated, because
365 translation is not a problem in the 8-bit environment, in which all GL
366 characters are ASCII and no translation takes place. So we don't check
367 for the 8-bit single-character versions of CSI, DCS, OSC, APC, or ST.
368 Here is the ANSI sequence recognizer state table, followed by the code
372 CAN = Cancel 01/08 Ctrl-X
373 SUB = Substitute 01/10 Ctrl-Z
374 DCS = Device Control Sequence 01/11 05/00 ESC P
375 CSI = Control Sequence Introducer 01/11 05/11 ESC [
376 ST = String Terminator 01/11 05/12 ESC \
377 OSC = Operating System Command 01/11 05/13 ESC ]
378 PM = Privacy Message 01/11 05/14 ESC ^
379 APC = Application Program Command 01/11 05/15 ESC _
381 ANSI escape sequence recognizer:
383 State Input New State ; Commentary
385 NORMAL (start) ; Start in NORMAL state
387 (any) CAN NORMAL ; ^X cancels
388 (any) SUB NORMAL ; ^Z cancels
390 NORMAL ESC GOTESC ; Begin escape sequence
391 NORMAL other ; NORMAL control or graphic character
393 GOTESC ESC ; Start again
394 GOTESC [ GOTCSI ; CSI
395 GOTESC P STRING ; DCS introducer, consume through ST
396 GOTESC ] STRING ; OSC introducer, consume through ST
397 GOTESC ^ STRING ; PM introducer, consume through ST
398 GOTESC _ STRING ; APC introducer, consume through ST
399 GOTESC 0..~ NORMAL ; 03/00 through 17/14 = Final character
400 GOTESC other ESCSEQ ; Intermediate or ignored control character
402 ESCSEQ ESC GOTESC ; Start again
403 ESCSEQ 0..~ NORMAL ; 03/00 through 17/14 = Final character
404 ESCSEQ other ; Intermediate or ignored control character
406 GOTCSI ESC GOTESC ; Start again
407 GOTCSI @..~ NORMAL ; 04/00 through 17/14 = Final character
408 GOTCSI other ; Intermediate char or ignored control char
410 STRING ESC TERMIN ; Maybe have ST
411 STRING other ; Consume all else
413 TERMIN \ NORMAL ; End of string
414 TERMIN other STRING ; Still in string
417 #ifdef XPRINT /* Transparent print support */
419 We can't just print each byte as it comes in because then the printer-off
420 sequence would be sent to the printer. Thus we have to buffer up escape
421 sequences and print them only when they are complete AND we know they are
422 not the printer-off sequence. All printing is done via zsoutx(ZMFILE,s,n).
423 This allows for strings that contain NULs. Don't mix calls to zsoutx() with
424 calls to zchout(), or the output will be scrambled. Also note that when
425 printing a saved-up escape sequence, we never print its final character
426 because that will be printed in the mainline code, upon return from
427 chkaes(). Note that the printer-on sequence is passed to the screen; this
428 is unavoidable, since we don't know what it is until after we get to the
429 end, and for screen display purposes we can't buffer up escape sequences
430 for numerous reasons. Therefore we also must output the printer-off
431 sequence, otherwise a real terminal or emulator will be stuck in print mode.
435 static char escbuf[ESCBUFLEN+1] = { NUL, NUL };
436 static int escbufc = 0;
437 static int dontprint = 0;
440 printon() { /* Turn printing on */
443 extern int printpipe, noprinter;
444 extern char * printername;
447 debug(F110,"PRINTER ON NOPRINTER","",0);
460 debug(F110,"PRINTER DEFAULT",p,0);
462 debug(F111,"PRINTER ON",p,pp);
463 if (pp) { /* Printing to pipe */
465 } else { /* Append to file */
467 xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
468 xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = NUL;
470 x = zopeno(ZMFILE,p,NULL,&xx);
472 debug(F101,"PRINTER OPEN","",x);
477 printoff() { /* Turn printing off */
479 extern int noprinter;
482 debug(F100,"PRINTER OFF NOPRINTER","",0);
485 debug(F100,"PRINTER OFF","",0);
488 debug(F101,"PRINTER CLOSE","",x);
495 C H K A E S -- Check ANSI Escape Sequence.
497 Call with EACH character in input stream.
498 src = 0 means c is incoming from remote; 1 = char from keyboard.
499 Sets global inesc[src] variable according to escape sequence state.
500 Returns 0 normally, 1 if an APC sequence is to be executed.
501 Handles transparent printing internally.
505 chkaes(char c, int src)
507 chkaes(c,src) char c; int src;
508 #endif /* CK_ANSIC */
511 debug(F111,"chkaes entry inesc",ckitoa(src),inesc[src]);
512 debug(F101,"chkaes c","",c);
514 if (src < 0 || src > 1) /* Don't allow bad args. */
517 oldesc[src] = inesc[src]; /* Remember previous state */
520 if (inesc[src] && !src) { /* Save up escape seq for printing */
521 if (!c) return(0); /* Ignore NULs */
522 if (escbufc < ESCBUFLEN) {
523 escbuf[escbufc++] = c;
524 escbuf[escbufc] = NUL;
525 debug(F111,"ESCBUF 1",escbuf,escbufc);
526 } else { /* Buffer overrun */
527 if (printing && escbufc) /* Print what's there so far */
528 zsoutx(ZMFILE,escbuf,escbufc);
529 escbufc = 1; /* clear it out */
530 escbuf[0] = c; /* and start off fresh buffer */
531 escbuf[1] = NUL; /* with this character. */
536 if (c == CAN || c == SUB) { /* CAN and SUB cancel any sequence */
539 if (printing && escbufc > 1)
540 zsoutx(ZMFILE,escbuf,escbufc-1);
541 escbufc = 0; /* Clear buffer */
545 inesc[src] = ES_NORMAL;
546 } else /* Otherwise */
548 switch (inesc[src]) { /* enter state switcher */
549 case ES_NORMAL: /* NORMAL state */
550 if (c == ESC) { /* Got an ESC */
551 inesc[src] = ES_GOTESC; /* Change state to GOTESC */
554 escbufc = 1; /* Clear escape sequence buffer */
555 escbuf[0] = c; /* and deposit the ESC */
557 debug(F111,"ESCBUF 2",escbuf,escbufc);
561 break; /* Otherwise stay in NORMAL state */
563 case ES_GOTESC: /* GOTESC state - prev char was ESC*/
564 if (c == '[') { /* Left bracket after ESC is CSI */
565 inesc[src] = ES_GOTCSI; /* Change to GOTCSI state */
566 } else if (c == 'P' || (c > 0134 && c < 0140)) { /* P, ], ^, or _ */
567 inesc[src] = ES_STRING; /* Switch to STRING-absorption state */
569 debug(F111,"ESCBUF STRING",escbuf,escbufc);
572 /* If APC not disabled */
573 if (!src && c == '_' && (apcstatus & APC_ON)) {
574 debug(F100,"CONNECT APC begin","",0);
575 apcactive = APC_REMOTE; /* Set APC-Active flag */
576 apclength = 0; /* and reset APC buffer pointer */
579 } else if (c > 057 && c < 0177) { /* Final character '0' thru '~' */
580 inesc[src] = ES_NORMAL; /* Back to normal */
583 if (printing && escbufc > 1) {
584 /* Dump esc seq buf to printer */
585 zsoutx(ZMFILE,escbuf,escbufc-1);
586 debug(F111,"ESCBUF PRINT 1",escbuf,escbufc);
589 escbufc = 0; /* Clear parameter buffer */
593 } else if (c != ESC) { /* ESC in an escape sequence... */
594 inesc[src] = ES_ESCSEQ; /* starts a new escape sequence */
596 break; /* Intermediate or ignored ctrl char */
598 case ES_ESCSEQ: /* ESCSEQ -- in an escape sequence */
599 if (c > 057 && c < 0177) { /* Final character '0' thru '~' */
600 inesc[src] = ES_NORMAL; /* Return to NORMAL state. */
603 if (printing && escbufc > 1) {
604 zsoutx(ZMFILE,escbuf,escbufc-1);
605 debug(F111,"ESCBUF PRINT 2",escbuf,escbufc);
607 escbufc = 0; /* Clear escseq buffer */
611 } else if (c == ESC) { /* ESC ... */
612 inesc[src] = ES_GOTESC; /* starts a new escape sequence */
614 break; /* Intermediate or ignored ctrl char */
616 case ES_GOTCSI: /* GOTCSI -- In a control sequence */
617 if (c > 077 && c < 0177) { /* Final character '@' thru '~' */
619 if (!src && tt_print) { /* Printer enabled? */
620 if (c == 'i') { /* Final char is "i"? */
621 char * p = (char *) (escbuf + escbufc - 4);
622 if (!strncmp(p, "\033[5i", 4)) { /* Turn printer on */
624 } else if (!strncmp(p, "\033[4i", 4)) { /* Or off... */
626 printoff(); /* Turn off printer. */
628 for (i = 0; i < escbufc; i++) /* And output the */
629 ckcputc(escbuf[i]); /* sequence. */
630 } else if (printing && escbufc > 1) {
631 zsoutx(ZMFILE,escbuf,escbufc-1);
632 debug(F011,"ESCBUF PRINT 3",escbuf,escbufc);
634 } else if (printing && escbufc > 1) {
635 zsoutx(ZMFILE,escbuf,escbufc-1);
636 debug(F111,"ESCBUF PRINT 4",escbuf,escbufc);
640 escbufc = 0; /* Clear esc sequence buffer */
644 inesc[src] = ES_NORMAL; /* Return to NORMAL. */
645 } else if (c == ESC) { /* ESC ... */
646 inesc[src] = ES_GOTESC; /* starts over. */
650 case ES_STRING: /* Inside a string */
651 if (c == ESC) /* ESC may be 1st char of terminator */
652 inesc[src] = ES_TERMIN; /* Go see. */
654 else if (apcactive) { /* If in APC */
655 if (apclength < apcbuflen) { /* and there is room... */
656 apcbuf[apclength++] = c; /* deposit this character. */
657 } else { /* Buffer overrun */
658 apcactive = 0; /* Discard what we got */
659 apclength = 0; /* and go back to normal */
660 apcbuf[0] = 0; /* Not pretty, but what else */
661 inesc[src] = ES_NORMAL; /* can we do? (ST might not come) */
665 break; /* Absorb all other characters. */
667 case ES_TERMIN: /* Maybe a string terminator */
668 if (c == '\\') { /* which must be backslash */
669 inesc[src] = ES_NORMAL; /* If so, back to NORMAL */
672 if (printing && escbufc > 1) { /* If printing... */
673 /* Print esc seq buffer */
674 zsoutx(ZMFILE,escbuf,escbufc-1);
675 debug(F111,"ESCBUF PRINT 5",escbuf,escbufc);
677 escbufc = 0; /* Clear escseq buffer */
682 if (!src && apcactive) { /* If it was an APC string, */
683 debug(F101,"CONNECT APC terminated","",c);
684 apcbuf[apclength] = NUL; /* terminate it and then ... */
688 } else { /* It's not a backslash so... */
689 inesc[src] = ES_STRING; /* back to string absorption. */
691 if (apcactive) { /* In APC string */
692 if (apclength+1 < apcbuflen) { /* If enough room */
693 apcbuf[apclength++] = ESC; /* deposit the Esc */
694 apcbuf[apclength++] = c; /* and this character too. */
695 } else { /* Buffer overrun */
699 inesc[src] = ES_NORMAL;
705 debug(F111,"chkaes exit inesc",ckitoa(src),inesc[src]);
708 #endif /* NOESCSEQ */
710 /* C K C P U T C -- C-Kermit CONNECT Put Character to Screen */
712 Output is buffered to avoid slow screen writes on fast connections.
715 ckcputf() { /* Dump the console output buffer */
717 if (obc > 0) /* If we have any characters, */
718 x = conxo(obc,obuf); /* dump them, */
719 obp = obuf; /* reset the pointer */
720 obc = 0; /* and the counter. */
721 return(x); /* Return conxo's return code */
725 NOTE: This is probably the right place for character-set translation,
726 rather than down below in the mainline code. ckcputc() would act like
727 xpnbyte() in ckcfns.c, and ckcgetc() would act like xgnbyte(). This
728 would shield the rest of the code from all the complexities of many-to-one
729 and one-to-many conversions, and would allow handling of Kanji and other
730 CJK sets along with UTF-8 and the rest.
736 *obp++ = c & 0xff; /* Deposit the character */
737 obc++; /* Count it */
738 if (ibc == 0 || /* If input buffer about empty */
739 obc == OBUFL) { /* or output buffer full */
740 debug(F101,"CONNECT CKCPUTC obc","",obc);
741 x = conxo(obc,obuf); /* dump the buffer, */
742 obp = obuf; /* reset the pointer */
743 obc = 0; /* and the counter. */
744 return(x); /* Return conxo's return code */
748 /* C K C G E T C -- C-Kermit CONNECT Get Character */
750 Buffered read from communication device.
751 Returns the next character, refilling the buffer if necessary.
752 On error, returns ttinc's return code (see ttinc() description).
753 Dummy argument for compatible calling conventions with ttinc()
754 so a pointer to this function can be passed to tn_doop().
757 ckcgetc(dummy) int dummy; {
760 extern int ssl_active_flag, tls_active_flag;
764 /* No buffering for possibly encrypted connections */
765 if (network && IS_TELNET() && TELOPT_ME(TELOPT_AUTHENTICATION))
767 #endif /* CK_ENCRYPTION */
769 if (ssl_active_flag || tls_active_flag)
773 if (ibc < 1) { /* Need to refill buffer? */
774 ibc = 0; /* Yes, reset count */
775 ibp = ibuf; /* and buffer pointer */
776 c = ttinc(0); /* Read one character, blocking */
777 if (c < 0) { /* If error, return error code */
779 } else { /* Otherwise, got one character */
780 *ibp++ = c; /* Advance buffer pointer */
781 ibc++; /* and count. */
783 if ((n = ttchk()) > 0) { /* Any more waiting? */
784 if (n > (IBUFL - ibc)) /* Get them all at once. */
785 n = IBUFL - ibc; /* Don't overflow buffer */
786 if ((n = ttxin(n,(CHAR *)ibp)) > 0) {
787 ibc += n; /* Advance counter */
789 } else if (n < 0) { /* Error? */
790 return(n); /* Return the error code */
792 ibp = ibuf; /* Point to beginning of buffer */
794 c = *ibp++ & 0xff; /* Get next character from buffer */
795 ibc--; /* Reduce buffer count */
796 /* debug(F000,"CKCGETC","",c); */
797 return(c); /* Return the character */
801 Keyboard handling, buffered for speed, which is needed when C-Kermit is
802 in CONNECT mode between two other computers that are transferring data.
804 static char *kbp; /* Keyboard input buffer pointer */
805 static int kbc; /* Keyboard input buffer count */
807 #ifdef CK_SMALL /* Keyboard input buffer length */
808 #define KBUFL 32 /* Small for PDP-11 UNIX */
810 #define KBUFL 257 /* Regular kernel size for others */
811 #endif /* CK_SMALL */
814 static char *kbuf = NULL;
816 static char kbuf[KBUFL];
819 /* Macro for reading keystrokes. */
821 #define CONGKS() (((--kbc)>=0) ? ((int)(*kbp++) & 0377) : kbget())
824 Note that we call read() directly here, normally a no-no, but in this case
825 we know it's UNIX and we're only doing what coninc(0) would have done,
826 except we're reading a block of characters rather than just one. There is,
827 at present, no conxin() analog to ttxin() for chunk reads, and instituting
828 one would only add function-call overhead as it would only be a wrapper for
829 a read() call anyway.
831 Another note: We stick in this read() till the user types something.
832 But we know they already did, since select() said so. Therefore something
833 would need to be mighty wrong before we get stuck here.
835 static int /* Keyboard buffer filler */
838 int tries = 10; /* If read() is interrupted, */
840 while (tries-- > 0) { /* try a few times... */
842 kbc = conchk(); /* How many chars waiting? */
843 debug(F101,"kbget kbc","",kbc);
845 kbc = 1; /* If none or dunno, wait for one. */
846 else if (kbc > KBUFL) /* If too many, */
847 kbc = KBUFL; /* only read this many. */
848 if ((kbc = read(0, kbuf, kbc)) < 1) { /* Now read it/them. */
849 debug(F101,"CONNECT kbget errno","",errno); /* Got an error. */
851 if (errno == EINTR) /* Interrupted system call. */
852 continue; /* Try again, up to limit. */
853 else /* Something else. */
855 return(-1); /* Pass along read() error. */
858 else { ok = 1; break; }
862 kbp = kbuf; /* Adjust buffer pointer, */
864 return((int)(*kbp++) & 0377); /* and return first character. */
869 * CreateSocketPair --
871 * This procedure creates a connected socket pair
874 * 0 if OK, the error if not OK.
880 socketpair(int *pair) {
883 struct sockaddr_in serv_addr, cli_addr;
884 extern char myipaddr[];
886 debug(F110,"socketpair",myipaddr,0);
888 if (myipaddr[0] == 0)
891 servsock = socket(AF_INET, SOCK_STREAM, 0);
895 debug(F111,"socketpair","socket",servsock);
897 memset(&serv_addr, 0, sizeof(serv_addr));
898 serv_addr.sin_family = AF_INET;
899 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
900 serv_addr.sin_port = htons(0);
902 val = sizeof(serv_addr);
903 if (bind(servsock, (struct sockaddr *) &serv_addr, val) < 0) {
904 closesocket(servsock);
907 debug(F111,"socketpair","bind",0);
910 debug(F111,"socketpair","listen",0);
912 if (getsockname(servsock, (struct sockaddr *) &serv_addr, &val) < 0) {
913 closesocket(servsock);
916 debug(F111,"socketpair","getsockname",0);
918 pair[0] = socket(AF_INET, SOCK_STREAM, 0);
920 closesocket(servsock);
923 debug(F111,"socketpair","socket",pair[0]);
925 memset(&cli_addr, 0, sizeof(cli_addr));
926 cli_addr.sin_family = AF_INET;
927 cli_addr.sin_addr.s_addr = inet_addr(myipaddr[0]?myipaddr:"127.0.0.1");
928 cli_addr.sin_port = serv_addr.sin_port;
930 if (connect(pair[0],(struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
931 closesocket(pair[0]);
932 closesocket(servsock);
935 debug(F111,"socketpair","connect",0);
937 pair[1] = accept(servsock, (struct sockaddr *) &serv_addr, &val);
939 closesocket(pair[0]);
940 closesocket(servsock);
943 debug(F111,"socketpair","accept",pair[1]);
945 closesocket(servsock);
946 debug(F111,"socketpair","closesocket",0);
951 kbdread(void * param) {
952 int sock = (int) param;
956 debug(F111,"kbdread","sock",sock);
959 rc = read(fileno(stdin), &ch, 1); /* Read a character. */
961 rc = send(sock,&ch,1,0);
962 /* debug(F000,"kbdread","send()",ch); */
963 printf("\r\ngot: %c rc = %d\r\n",ch,rc);
967 debug(F110,"kbdread","terminating",0);
974 learnchar(c) int c; { /* Learned script keyboard character */
978 if (!learning || !learnfp)
981 switch (learnst) { /* Learn state... */
982 case 0: /* Neutral */
984 if (learnbc > 0) { /* Have net characters? */
985 char buf[LEARNBUFSIZ];
989 t = (ULONG) time(0); /* Calculate INPUT timeout */
991 j += (j / 4) > 0 ? (j / 4) : 1; /* Add some slop */
992 if (j < 2) j = 2; /* 2 seconds minimum */
994 fputs("\nINPUT ",learnfp); /* Give INPUT command for them */
995 fputs(ckitoa(j),learnfp);
1000 if (learnbc < LEARNBUFSIZ) { /* Circular buffer */
1001 n = learnbc; /* hasn't wrapped yet. */
1004 j = 0; /* Copy to linear buffer */
1005 for (i = 0; i < n; i++) { /* Number of chars in circular buf */
1007 cc = learnbuf[(learnbp + i) % LEARNBUFSIZ];
1009 /* Later account for prompts that end with a newline? */
1011 if (cc == CR && j > 0) {
1017 for (i = 0; i < j; i++) { /* Now copy out the buffer */
1018 cc = buf[i]; /* interpreting control chars */
1019 if (cc == 0) { /* We don't INPUT NULs */
1021 } else if (cc < SP || /* Controls need quoting */
1022 (cc > 126 && cc < 160)) {
1023 ckmakmsg(xbuf,8,"\\{",ckitoa((int)cc),"}",NULL);
1024 fputs(xbuf,learnfp);
1025 } else { /* Plain character */
1029 fputs("}\nIF FAIL STOP 1 INPUT timeout",learnfp);
1033 fputs("\nPAUSE 1\nOUTPUT ",learnfp); /* Emit OUTPUT and fall thru */
1035 case 2: /* Already in Keyboard state */
1037 fputs("\\N",learnfp);
1038 } else if (c == -7) {
1039 fputs("\\B",learnfp);
1040 } else if (c == -8) {
1041 fputs("\\L",learnfp);
1042 } else if (c < SP || (c > 126 && c < 160)) {
1043 ckmakmsg(xbuf,8,"\\{",ckitoa((int)c),"}",NULL);
1044 fputs(xbuf,learnfp);
1050 #endif /* CKLEARN */
1052 static int printbar = 0;
1054 #define OUTXBUFSIZ 15
1055 static CHAR inxbuf[OUTXBUFSIZ+1]; /* Host-to-screen expansion buffer */
1056 static int inxcount = 0; /* and count */
1057 static CHAR outxbuf[OUTXBUFSIZ+1]; /* Keyboard-to-host expansion buf */
1058 static int outxcount = 0; /* and count */
1062 int rc = 0; /* Return code: 0 = fail, 1 = OK */
1063 int i, x = 0, prev = -1; /* Reason code in cx_status */
1066 #endif /* CKLEARN */
1067 register int c = -1, c2, csave; /* Characters */
1069 int tx; /* For Telnet negotiations */
1071 int apcrc = 0; /* For APC and transparent print */
1072 int n, kbin, scrnout; /* select() items... */
1073 fd_set in, out, err; /* File descriptor sets */
1074 int gotnet = 0; /* Flag for net ready to read */
1075 int gotkbd = 0; /* Flag for keyboard ready to read */
1076 int oldprt = 0; /* Used with printing */
1078 char cbuf[2]; /* Ditto */
1081 int tid = 0; /* Thread ID */
1082 int pair[2]; /* Socket Pair */
1087 cx_status = CSX_INTERNAL;
1092 /* Create a socket pair to be used for the keyboard input */
1093 if (socketpair(pair)) {
1094 debug(F110,"conect","unable to create socket pair",0);
1097 debug(F111,"connect","socket pair[0]",pair[0]);
1098 debug(F111,"connect","socket pair[1]",pair[1]);
1100 /* Assign one end of the socket to kbin */
1102 tid = spawn_thread(kbdread,
1103 "Kbd to Socket Pair",
1108 debug(F110,"connect","tid",tid);
1111 kbin = fileno(stdin); /* stdin file descriptor */
1114 scrnout = fileno(stdout); /* stdout file descriptor */
1117 makestr(&triggerval,NULL); /* Reset trigger */
1118 #endif /* CK_TRIGGER */
1121 escbufc = 0; /* Reset esc-sequence buffer */
1126 ttimoff(); /* Turn off any timer interrupts */
1127 if (!local) { /* Be sure we're not in remote mode */
1130 if (ftpisconnected())
1131 printf("Sorry, you can't CONNECT to an FTP server\n");
1134 printf("Sorry, you must SET LINE or SET HOST first\n");
1136 printf("Sorry, you must SET LINE first\n");
1137 #endif /* NETCONN */
1140 if (speed < 0L && network == 0 && ttfdflg == 0) {
1141 printf("Sorry, you must SET SPEED first\n");
1145 if (network && !ttpipe && (nettype != NET_TCPB && nettype != NET_PTY)) {
1146 printf("Sorry, network type not supported\n");
1149 #endif /* TCPSOCKET */
1153 if (!(ibuf = malloc(IBUFL+1))) { /* Allocate input line buffer */
1154 printf("Sorry, CONNECT input buffer can't be allocated\n");
1162 if (!(obuf = malloc(OBUFL+1))) { /* Allocate output line buffer */
1163 printf("Sorry, CONNECT output buffer can't be allocated\n");
1171 if (!(kbuf = malloc(KBUFL+1))) { /* Allocate keyboard input buffer */
1172 printf("Sorry, CONNECT keyboard buffer can't be allocated\n");
1177 if (!(temp = malloc(TMPLEN+1))) { /* Allocate temporary buffer */
1178 printf("Sorry, CONNECT temporary buffer can't be allocated\n");
1185 #endif /* DYNAMIC */
1187 kbp = kbuf; /* Always clear these. */
1188 *kbp = NUL; /* No need to preserve them between */
1189 kbc = 0; /* CONNECT sessions. */
1193 debug(F101,"CONNECT conect entry ttyfd","",ttyfd);
1194 debug(F101,"CONNECT conect entry ibc","",ibc);
1195 debug(F101,"CONNECT conect entry obc","",obc);
1196 debug(F101,"CONNECT conect entry kbc","",kbc);
1198 debug(F110,"CONNECT conect trigger",tt_trigger[0],0);
1199 #endif /* CK_TRIGGER */
1202 debug(F101,"CONNECT conect entry ttchk","",n);
1207 if (ttyfd < 0) { /* If communication device not open */
1210 debug(F111,"CONNECT le_inbuf()","ttyfd < 0",n);
1219 #endif /* TTLEBUF */
1221 debug(F101,"CONNECT ttnproto","",ttnproto);
1222 debug(F111,"CONNECT opening",ttname,0); /* Open it now */
1225 network ? -nettype : mdmtyp,
1228 ckmakmsg(temp,TMPLEN,"Sorry, can't open ",ttname,NULL,NULL);
1230 debug(F110,"CONNECT open failure",ttname,0);
1235 /* If peer is in Kermit server mode, return now. */
1236 if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start) {
1237 cx_status = CSX_IKSD;
1240 #endif /* IKS_OPTION */
1242 dohangup = 0; /* Hangup not requested yet */
1253 #ifdef CK_ENCRYPTION
1254 extern int me_encrypt, u_encrypt;
1255 if (ck_tn_encrypting() && ck_tn_decrypting())
1256 printf("SECURE connection to host %s",ttname);
1258 #endif /* CK_ENCRYPTION */
1259 if (ttpipe || ttpty)
1260 printf("Connecting via command \"%s\"",ttname);
1262 printf("Connecting to host %s",ttname);
1264 #endif /* NETCONN */
1265 printf("Connecting to %s",ttname);
1266 if (speed > -1L) printf(", speed %ld",speed);
1269 #endif /* NETCONN */
1273 printf("Type the escape character followed by C to get back,\r\n");
1274 printf("or followed by ? to see other options.\r\n");
1276 printf(".\r\n\nESCAPE CHARACTER IS DISABLED\r\n\n");
1285 s = slogts ? "timestamped-text" : "text"; break;
1289 printf("Session Log: %s, %s\r\n",sesfil,s);
1291 if (debses) printf("Debugging Display...)\r\n");
1294 /* Condition console terminal and communication line */
1296 if (conbin((char)escape) < 0) {
1297 printf("Sorry, can't condition console terminal\n");
1301 debug(F101,"CONNECT cmask","",cmask);
1302 debug(F101,"CONNECT cmdmsk","",cmdmsk);
1303 debug(F101,"CONNECT speed before ttvt","",speed);
1304 if ((n = ttvt(speed,flow)) < 0) { /* Enter "virtual terminal" mode */
1306 debug(F101,"CONNECT ttvt","",n);
1307 tthang(); /* Hang up and close the device. */
1310 if (ttopen(ttname, /* Open it again... */
1312 network ? -nettype : mdmtyp,
1315 cx_status = CSX_INTERNAL;
1316 ckmakmsg(temp,TMPLEN,"Sorry, can't reopen ",ttname,NULL,NULL);
1321 if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start) {
1322 cx_status = CSX_IKSD;
1325 #endif /* IKS_OPTION */
1327 if (ttvt(speed,flow) < 0) { /* Try virtual terminal mode again. */
1328 conres(); /* Failure this time is fatal. */
1329 printf("Sorry, Can't condition communication line\n");
1330 cx_status = CSX_INTERNAL;
1335 debug(F101,"CONNECT ttvt ok, escape","",escape);
1337 /* Despite ttvt() this is still needed in HP-UX */
1338 /* because of the HP-9000 <RESET> key.*/
1340 signal(SIGINT, SIG_IGN);
1341 signal(SIGQUIT, SIG_IGN);
1343 debug(F101,"CONNECT carrier-watch","",carrier);
1347 #endif /* TN_COMPORT */
1348 ) && (carrier != CAR_OFF)) {
1351 debug(F100,"CONNECT ttgmdm","",x);
1352 if ((x > -1) && !(x & BM_DCD)) {
1355 #endif /* NOHINTS */
1356 debug(F100,"CONNECT ttgmdm CD test fails","",x);
1358 printf("?Carrier required but not detected.\n");
1360 cx_status = CSX_CARRIER;
1363 printf("***********************************\n");
1364 printf(" Hint: To CONNECT to a serial device that\n");
1365 printf(" is not presenting the Carrier Detect signal,\n");
1366 printf(" first tell C-Kermit to:\n\n");
1367 printf(" SET CARRIER-WATCH OFF\n\n");
1368 printf("***********************************\n\n");
1369 #endif /* NOHINTS */
1372 debug(F100,"CONNECT ttgmdm ok","",0);
1375 /* Now we are connected. */
1377 if (msgflg || printbar)
1378 printf("----------------------------------------------------\r\n");
1382 /* Set up character set translations */
1384 unicode = 0; /* Assume Unicode won't be involved */
1385 tcs = 0; /* "Transfer" or "Other" charset */
1386 sxo = rxo = NULL; /* Initialize byte-to-byte functions */
1389 if (tcsr != tcsl) { /* Remote and local sets differ... */
1391 if (tcsr == FC_UTF8 || /* Remote charset is UTF-8 */
1392 tcsl == FC_UTF8) { /* or local one is. */
1393 xuf = xl_ufc[tcsl]; /* Incoming Unicode to local */
1394 if (xuf || tcsl == FC_UTF8) {
1395 tcs = (tcsr == FC_UTF8) ? tcsl : tcsr; /* The "other" set */
1396 xfu = xl_fcu[tcs]; /* Local byte to remote Unicode */
1398 unicode = (tcsr == FC_UTF8) ? 1 : 2;
1400 tcssize = fcsinfo[tcs].size; /* Size of other character set. */
1402 #endif /* UNICODE */
1403 tcs = gettcs(tcsr,tcsl); /* Get intermediate set. */
1404 sxo = xls[tcs][tcsl]; /* translation function */
1405 rxo = xlr[tcs][tcsr]; /* pointers for output functions */
1406 sxi = xls[tcs][tcsr]; /* and for input functions. */
1407 rxi = xlr[tcs][tcsl];
1410 #endif /* UNICODE */
1413 This is to prevent use of zmstuff() and zdstuff() by translation functions.
1414 They only work with disk i/o, not with communication i/o. Luckily Russian
1415 translation functions don't do any stuffing...
1419 if (language != L_RUSSIAN)
1420 #endif /* NOCYRIL */
1421 language = L_USASCII;
1426 debug(F101,"CONNECT tcs","",tcs);
1427 debug(F101,"CONNECT tcsl","",tcsl);
1428 debug(F101,"CONNECT tcsr","",tcsr);
1429 debug(F101,"CONNECT fcsinfo[tcsl].size","",fcsinfo[tcsl].size);
1430 debug(F101,"CONNECT fcsinfo[tcsr].size","",fcsinfo[tcsr].size);
1431 debug(F101,"CONNECT unicode","",unicode);
1434 #endif /* COMMENT */
1437 #ifndef XYZ_INTERNAL
1439 extern int binary; /* See about ZMODEM autodownloads */
1441 s = binary ? ptab[PROTO_Z].p_b_rcmd : ptab[PROTO_Z].p_t_rcmd;
1443 zmdlok = (*s != NUL); /* OK if we have external commands */
1445 #endif /* XYZ_INTERNAL */
1450 We need to activate the escape-sequence recognition feature when:
1451 (a) translation is elected, AND
1452 (b) the local and/or remote set is a 7-bit set other than US ASCII.
1454 SET TERMINAL APC is not OFF (handled in the next statement).
1456 escseq = (tcs != TC_TRANSP) && /* Not transparent */
1457 (fcsinfo[tcsl].size == 128 || fcsinfo[tcsr].size == 128) && /* 7 bits */
1458 (fcsinfo[tcsl].code != FC_USASCII); /* But not ASCII */
1459 #endif /* NOESCSEQ */
1460 #endif /* NOCSETS */
1464 escseq = escseq || (apcstatus & APC_ON);
1465 apcactive = 0; /* An APC command is not active */
1466 apclength = 0; /* ... */
1471 inesc[0] = ES_NORMAL; /* Initial state of recognizer */
1472 inesc[1] = ES_NORMAL;
1473 debug(F101,"CONNECT escseq","",escseq);
1474 #endif /* NOESCSEQ */
1476 if (ttyfd > -1) { /* (just in case...) */
1477 what = W_CONNECT; /* Keep track of what we're doing */
1481 if (learning) { /* Learned script active... */
1482 learnbp = 0; /* INPUT buffer pointer */
1483 learnbc = 0; /* INPUT buffer count */
1484 learnst = 0; /* State (0 = neutral, none) */
1485 learnt1 = (ULONG) time(0);
1487 #endif /* CKLEARN */
1490 idlelimit = tt_idlelimit;
1491 #endif /* CKTIDLE */
1493 while (active) { /* Big loop... */
1494 debug(F100,"CONNECT top of loop","",0);
1495 FD_ZERO(&in); /* Clear select() structs */
1499 gotnet = ttpeek(); /* Something sitting in ckutio buf */
1500 debug(F101,"CONNECT ttpeek","",gotnet);
1504 !kmptr /* Check for key macro active */
1507 #endif /* NOSETKEY */
1509 if (obc) { /* No key macro - set up for select */
1510 FD_SET(ttyfd, &out); /* Have stuff to send to net */
1512 FD_SET(kbin, &in); /* Need to read stuff from keyboard */
1515 if (!(ibc || gotnet > 0))
1516 FD_SET(ttyfd, &in); /* Need to read stuff from net */
1518 if (ibc || gotnet > 0) {
1519 FD_SET(scrnout, &out); /* Have stuff to put on screen */
1521 FD_SET(ttyfd, &in); /* Need to read stuff from net */
1524 FD_SET(ttyfd, &err);
1526 fwdx_init_fd_set(&in);
1527 #endif /* CK_FORWARD_X */
1529 /* Wait till the first one of the above is ready for i/o */
1530 /* or TERM IDLE-SEND is active and we time out. */
1534 /* This really could be moved out of the loop... */
1535 if (idlelimit) { /* Idle timeout set */
1537 if (idlelimit > 0) { /* Positive = sec */
1538 tv.tv_sec = (long) idlelimit;
1540 } else { /* Negative = millisec */
1541 long u = (0 - idlelimit);
1542 tv.tv_sec = u / 1000L;
1543 tv.tv_usec = ((u % 1000L) * 1000L);
1546 c = select(FD_SETSIZE,(int *)&in,(int *)&out,(int *)&err, &tv);
1548 c = select(FD_SETSIZE, &in, &out, &err, &tv);
1549 #endif /* INTSELECT */
1551 #endif /* CKTIDLE */
1553 c = select(FD_SETSIZE, (int *)&in, (int *)&out, (int *)&err, 0);
1555 c = select(FD_SETSIZE, &in, &out, &err, 0);
1556 #endif /* INTSELECT */
1559 if (c == 0) { /* Timeout */
1560 debug(F101,"CONNECT select() timeout","",tt_idleact);
1561 switch (tt_idleact) {
1562 case IDLE_HANG: { /* Hang up */
1569 tthang(); /* fall thru deliberately... */
1571 case IDLE_RET: /* Return to command mode */
1572 cx_status = CSX_IDLE;
1575 case IDLE_OUT: /* OUTPUT a string */
1577 int len = strlen(tt_idlestr);
1579 ttol((CHAR *)tt_idlestr,len);
1581 ttoc(NUL); /* No string, send a NUL */
1583 ttoc(NUL); /* No string, send a NUL */
1585 case IDLE_EXIT: /* Exit from Kermit */
1586 doexit(GOOD_EXIT,xitsta);
1588 case IDLE_TAYT: /* Send Telnet Are You There? */
1589 if (network && IS_TELNET()) {
1590 tnopt[0] = (CHAR) IAC;
1591 tnopt[1] = (CHAR) TN_AYT;
1593 if (ttol((CHAR *)tnopt,2) < 0)
1598 case IDLE_TNOP: /* Send Telnet NOP */
1599 if (network && IS_TELNET()) {
1600 tnopt[0] = (CHAR) IAC;
1601 tnopt[1] = (CHAR) TN_NOP;
1603 if (ttol((CHAR *)tnopt,2) < 0)
1610 #endif /* CKTIDLE */
1612 debug(F101,"CONNECT select() errno","",errno);
1613 /* A too-big first arg to select() gets EBADF */
1616 if (errno == EINTR) {
1626 if (FD_ISSET(scrnout, &out)) {
1627 debug(F100,"CONNECT SELECT scrnout","",0);
1633 fwdx_check_sockets(&in);
1634 #endif /* CK_FORWARD_X */
1636 if (FD_ISSET(ttyfd, &in)) { /* Read from net? */
1637 debug(F110,"CONNECT SELECT ttyfd","in",0);
1639 gotnet = 1; /* Net is ready */
1641 if (FD_ISSET(kbin, &in)) { /* Read from keyboard? */
1642 debug(F100,"CONNECT SELECT kbin","",0);
1644 gotkbd = 1; /* Keyboard is ready */
1646 if (FD_ISSET(ttyfd, &err)) {
1647 debug(F110,"CONNECT SELECT ttyfd","err",0);
1648 FD_CLR(ttyfd, &err);
1651 /* Special handling for HP-UX pty i/o */
1653 if (pty_trap_handler(ttyfd) > 0) {
1659 #endif /* HAVE_PTYTRAP */
1661 gotnet = 1; /* Net is ready (don't set if pty) */
1666 debug(F101,"CONNECT gotkbd","",gotkbd);
1667 debug(F101,"CONNECT kbc","",kbc);
1669 debug(F101,"CONNECT kmptr","",kmptr);
1670 #endif /* NOSETKEY */
1674 while (gotkbd || kbc > 0 /* If we have keyboard chars */
1677 #endif /* NOSETKEY */
1680 if (kmptr) { /* Have current macro? */
1681 debug(F100,"CONNECT kmptr non NULL","",0);
1682 if ((c = (CHAR) *kmptr++) == NUL) { /* Get char from it */
1683 debug(F100,"CONNECT macro empty, continuing","",0);
1684 kmptr = NULL; /* If no more chars, */
1685 continue; /* Reset pointer and continue */
1687 debug(F000,"CONNECT char from macro","",c);
1688 } else { /* No macro... */
1689 #endif /* NOSETKEY */
1693 if ((rc = recv(kbin,buf,1,0)) > 0)
1697 debug(F111,"recv","rc",rc);
1698 printf("\r\nrecv: %c rc=%d\r\n",buf[0],rc);
1701 c = CONGKS(); /* Yes, read from keyboard */
1703 gotkbd = 0; /* Turn off select() result flag */
1706 #endif /* NOSETKEY */
1712 cx_status = CSX_IOERROR;
1716 c &= cmdmsk; /* Do any requested masking */
1720 Note: kmptr is NULL if we got character c from the keyboard, and it is
1721 not NULL if it came from a macro. In the latter case, we must avoid
1724 if (!kmptr && macrotab[c]) { /* Macro definition for c? */
1725 debug(F000,"CONNECT macro key",macrotab[c],c);
1726 kmptr = macrotab[c]; /* Yes, set up macro pointer */
1727 continue; /* and restart the loop, */
1728 } else c = keymap[c]; /* else use single-char keymap */
1729 #endif /* NOSETKEY */
1733 #endif /* NOSETKEY */
1734 (tt_escape && ((c & 0xff) == escape))) { /* Escape char? */
1735 debug(F000,"CONNECT got escape","",c);
1737 if (recv(kbin,buf,1,0)>=0)
1742 c = CONGKS() & 0x7f; /* Read argument */
1744 doesc((char) c); /* Handle it */
1745 continue; /* Back to loop */
1747 csave = c; /* Save it before translation */
1748 /* for local echoing. */
1750 crflag = (c == CR); /* Remember if it was CR. */
1751 #endif /* CKLEARN */
1754 if (inesc[1] == ES_NORMAL) { /* If not inside escape seq.. */
1755 /* Translate character sets */
1758 if (unicode == 1) { /* Remote is UTF-8 */
1759 outxcount = b_to_u((CHAR)c,outxbuf,OUTXBUFSIZ,tcssize);
1760 outxbuf[outxcount] = NUL;
1761 } else if (unicode == 2) { /* Local is UTF-8 */
1763 x = u_to_b((CHAR)c);
1766 outxbuf[0] = (unsigned)(x & 0xff);
1768 outxbuf[outxcount] = NUL;
1770 #endif /* UNICODE */
1771 if (sxo) c = (*sxo)((char)c); /* Local-intermediate */
1772 if (rxo) c = (*rxo)((char)c); /* Intermediate-remote */
1775 outxbuf[outxcount] = NUL;
1778 #endif /* UNICODE */
1782 outxbuf[outxcount] = NUL;
1785 apcrc = chkaes((char)c,1);
1789 outxbuf[outxcount] = NUL;
1790 #endif /* NOCSETS */
1792 debug(F111,"OUTXBUF",outxbuf,outxcount);
1794 for (i = 0; i < outxcount; i++) {
1797 If Shift-In/Shift-Out is selected and we have a 7-bit connection,
1798 handle shifting here.
1800 if (sosi) { /* Shift-In/Out selected? */
1801 if (cmask == 0177) { /* In 7-bit environment? */
1802 if (c & 0200) { /* 8-bit character? */
1803 if (outshift == 0) { /* If not shifted, */
1804 ttoc(dopar(SO)); /* shift. */
1808 if (outshift == 1) { /* 7-bit character */
1809 ttoc(dopar(SI)); /* If shifted, */
1810 outshift = 0; /* unshift. */
1814 if (c == SO) outshift = 1; /* User typed SO */
1815 if (c == SI) outshift = 0; /* User typed SI */
1817 c &= cmask; /* Apply Kermit-to-host mask now. */
1818 if (c == '\015') { /* Carriage Return */
1820 if (tnlm) { /* TERMINAL NEWLINE ON */
1821 stuff = LF; /* Stuff LF */
1823 } else if (network && /* TELNET NEWLINE ON/OFF/RAW */
1825 switch (!TELOPT_ME(TELOPT_BINARY) ? tn_nlm : tn_b_nlm){
1836 ttoc(dopar('\015')); /* Send CR */
1837 if (duplex) conoc('\015'); /* Maybe echo CR */
1838 c = stuff; /* Char to stuff */
1843 /* If user types the 0xff character (TELNET IAC), it must be doubled. */
1845 if ((dopar((CHAR) c) == IAC) && /* IAC (0xff) */
1846 network && IS_TELNET()) { /* Send one now */
1847 ttoc((char)IAC); /* and the other one just below. */
1850 /* Send the character */
1852 x = ttoc((char)dopar((CHAR) c));
1855 if (learning) { /* Learned script active */
1856 if (crflag) { /* User typed CR */
1857 learnchar(CR); /* Handle CR */
1858 learnst = 0; /* Shift to Neutral */
1860 learnchar(c); /* Not CR */
1861 learnst = 2; /* Change state to Keyboard */
1864 #endif /* CKLEARN */
1865 if (duplex) { /* If half duplex, must echo */
1867 conol(dbchr(csave)); /* the original char */
1868 else /* not the translated one */
1870 if (seslog) { /* And maybe log it too */
1872 if (sessft == 0 && csave == '\r')
1878 perror("\r\nCan't send character");
1879 cx_status = CSX_IOERROR;
1885 if (FD_ISSET(ttyfd, &out)) {
1886 FD_CLR(ttyfd, &out);
1888 while (gotnet > 0 || ibc > 0) {
1891 c = ckcgetc(0); /* Get next character */
1892 /* debug(F101,"CONNECT c","",c); */
1893 if (c < 0) { /* Failed... */
1894 ckcputf(); /* Flush CONNECT output buffer */
1896 printf("\r\nCommunications disconnect ");
1900 /* This happens on Ultrix if there's no carrier */
1904 /* This happens on UTEK if there's no carrier */
1905 && errno != EWOULDBLOCK
1908 perror("\r\nCan't read character");
1909 #endif /* COMMENT */
1913 #endif /* NOSETBUF */
1915 tthang(); /* Hang up the connection */
1916 debug(F111,"CONNECT i/o error 1",ck_errstr(),errno);
1917 cx_status = CSX_HOSTDISC;
1922 if ((c == NUL) && network && IS_TELNET()) {
1923 if (prev == CR) { /* Discard <NUL> of <CR><NUL> if peer */
1924 if (!TELOPT_U(TELOPT_BINARY)) { /* not in binary mode */
1925 debug(F111,"CONNECT NUL",ckitoa(prev),c);
1926 ckcputf(); /* Flush screen output buffer */
1931 debug(F111,"CONNECT","c",c);
1932 debug(F111,"CONNECT","network",network);
1933 debug(F111,"CONNECT","IS_TELNET",IS_TELNET());
1934 if ((c == IAC) && network && IS_TELNET()) {
1935 #ifdef CK_ENCRYPTION
1936 int x_auth = TELOPT_ME(TELOPT_AUTHENTICATION);
1939 #endif /* CK_ENCRYPTION */
1940 int me_bin = TELOPT_ME(TELOPT_BINARY);
1941 int u_bin = TELOPT_U(TELOPT_BINARY);
1942 debug(F100,"CONNECT got IAC","",0);
1943 ckcputf(); /* Dump screen-output buffer */
1944 if ((tx = tn_doop((CHAR)(c & 0xff),duplex,ckcgetc)) == 0) {
1945 if (me_bin != TELOPT_ME(TELOPT_BINARY)) {
1946 me_bin = TELOPT_ME(TELOPT_BINARY);
1947 } else if (u_bin != TELOPT_U(TELOPT_BINARY)) {
1948 u_bin = TELOPT_U(TELOPT_BINARY);
1949 #ifdef CK_ENCRYPTION
1951 Here we have to push back any bytes we have read using block reads, so we
1952 can read them again using single-character reads, so they can be decrypted
1953 in case there was a switch to encryption in the block. Note that we can't
1954 handle switches in the encryption state itself this way -- which would be
1955 nice, since it would eliminate the need for single-character reads. Why?
1956 Because if a series of characters has already been decrypted that shouldn't
1957 have been, then (a) it's ruined, and (b) so is the state of the decryption
1960 } else if (TELOPT_ME(TELOPT_AUTHENTICATION) != 0 &&
1961 TELOPT_ME(TELOPT_AUTHENTICATION) != x_auth
1963 if (ttpushback((CHAR *)ibp,ibc) > -1) {
1967 #endif /* CK_ENCRYPTION */
1970 } else if (tx == -1) { /* I/O error */
1972 printf("\r\nCommunications disconnect ");
1975 #endif /* NOSETBUF */
1977 debug(F111,"CONNECT i/o error 2",ck_errstr(),errno);
1978 cx_status = CSX_IOERROR;
1980 } else if (tx == -2) { /* I/O error */
1982 printf("\r\nConnection closed by peer");
1985 #endif /* NOSETBUF */
1987 debug(F111,"CONNECT i/o error 3",ck_errstr(),errno);
1988 cx_status = CSX_IOERROR;
1990 } else if (tx == -3) { /* I/O error */
1992 printf("\r\nConnection closed due to telnet policy");
1995 #endif /* NOSETBUF */
1997 debug(F111,"CONNECT i/o error 4",ck_errstr(),errno);
1998 cx_status = CSX_IOERROR;
2000 } else if ((tx == 1) && (!duplex)) { /* ECHO change */
2001 duplex = 1; /* Turn on local echo */
2003 } else if ((tx == 2) && (duplex)) { /* ECHO change */
2006 } else if (tx == 3) { /* Quoted IAC */
2007 c = parity ? 127 : 255;
2010 else if (tx == 4) { /* IKS State Change */
2011 if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
2014 /* here we need to print a msg that the other */
2015 /* side is in SERVER mode and that REMOTE */
2016 /* commands should be used. And CONNECT mode */
2017 /* should be ended. */
2018 cx_status = CSX_IKSD;
2022 #endif /* IKS_OPTION */
2024 /* DO LOGOUT was received */
2026 printf("\r\nRemote Logout ");
2029 #endif /* NOSETBUF */
2030 debug(F100,"CONNECT Remote Logout","",0);
2031 cx_status = CSX_TRIGGER;
2034 continue; /* Negotiation OK, get next char. */
2038 /* I'm echoing for the remote */
2039 if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo)
2044 /* Learned script: Record incoming chars if not in Keyboard state */
2046 if (learning && learnst != 2) { /* Learned script active */
2047 learnbuf[learnbp++] = c; /* Save for INPUT command */
2048 if (learnbp >= LEARNBUFSIZ) /* in circular buffer */
2049 learnbp = 0; /* wrapping if at end. */
2050 learnbc++; /* Count this byte. */
2051 learnst = 1; /* State is Net. */
2053 #endif /* CKLEARN */
2055 if (debses) { /* Output character to screen */
2056 char *s; /* Debugging display... */
2060 } else { /* Regular display ... */
2061 c &= cmask; /* Apply Kermit-to-remote mask */
2062 if (seslog && sessft) /* If binary session log */
2063 logchar((char)c); /* log the character now. */
2067 Autodownload. Check for Kermit S packet prior to translation, since that
2068 can change the packet and make it unrecognizable (as when the terminal
2069 character set is an ISO 646 one)... Ditto for Zmodem start packet.
2071 if (autodl /* Autodownload enabled? */
2073 || TELOPT_SB(TELOPT_KERMIT).kermit.me_start
2074 #endif /* IKS_OPTION */
2078 if (kstartactive || c == stchr /* Kermit S or I packet? */
2080 || adl_kmode == ADLSTR /* Not used in C-Kermit */
2081 #endif /* COMMENT */
2083 k = kstart((CHAR)c);
2085 if (!k && zmdlok) /* Or an "sz" start? */
2086 k = zstart((CHAR)c);
2090 debug(F101,"CONNECT autodownload k","",k);
2091 if (k < 0) { /* Minus-Protocol? */
2093 goto noserver; /* Need server mode for this */
2095 ksign = 1; /* Remember */
2096 k = 0 - k; /* Convert to actual protocol */
2097 justone = 1; /* Flag for protocol module */
2098 #endif /* NOSERVER */
2101 k--; /* Adjust [kz]start's return value */
2107 /* Damage the packet so that it doesn't trigger */
2108 /* autodownload detection downstream. */
2110 int i, len = strlen((char *)ksbuf);
2111 for (i = 0; i < len; i++)
2117 for (i = 0; i < 3; i++)
2123 /* sprintf is safe here (builtin keywords) */
2125 "set proto %s, %s, set proto %s",
2127 ksign ? "server" : "receive",
2128 ptab[protocol].p_name
2130 apclength = strlen(apcbuf);
2131 debug(F111,"CONNECT ksbuf",ksbuf,k);
2132 debug(F110,"CONNECT autodownload",apcbuf,0);
2133 apcactive = APC_LOCAL;
2134 ckcputf(); /* Force screen update */
2135 cx_status = CSX_APC;
2139 Here's another way that doesn't require APC, but then we'll have to change
2140 all the other CONNECT modules, and then the mainline code that calls them.
2144 sstate = ksign ? 'x' : 'v';
2153 #endif /* NOSERVER */
2155 #endif /* CK_AUTODL */
2157 if (sosi) { /* Handle SI/SO */
2158 if (c == SO) { /* Shift Out */
2161 } else if (c == SI) { /* Shift In */
2165 if (inshift) c |= 0200;
2167 inxbuf[0] = c; /* In case there is no translation */
2168 inxcount = 1; /* ... */
2170 if (inesc[0] == ES_NORMAL /* If not in an escape sequence */
2171 && !printing /* and not in transparent print */
2172 ) { /* Translate character sets */
2175 if (unicode == 1) { /* Remote is UTF-8 */
2176 x = u_to_b((CHAR)c);
2179 else if (x == -2) { /* LS or PS */
2183 } else if (x == -9) { /* UTF-8 error */
2185 inxbuf[1] = u_to_b2();
2188 inxbuf[0] = (unsigned)(x & 0xff);
2191 } else if (unicode == 2) { /* Local is UTF-8 */
2192 inxcount = b_to_u((CHAR)c,inxbuf,OUTXBUFSIZ,tcssize);
2195 #endif /* UNICODE */
2196 if (sxi) c = (*sxi)((CHAR)c);
2197 if (rxi) c = (*rxi)((CHAR)c);
2201 #endif /* UNICODE */
2203 #endif /* NOCSETS */
2206 if (escseq) { /* If handling escape sequences */
2207 oldprt = printing; /* remember printer state */
2208 apcrc = chkaes((char)c,0); /* and update escseq state. */
2209 if (printing && !oldprt) /* If printer was turned on */
2210 continue; /* don't print final char of escseq */
2214 If we are handling APCs, we have several possibilities at this point:
2215 1. Ordinary character to be written to the screen.
2216 2. An Esc; we can't write it because it might be the beginning of an APC.
2217 3. The character following an Esc, in which case we write Esc, then char,
2218 but only if we have not just entered an APC sequence.
2220 if (escseq && (apcstatus & APC_ON)) {
2221 if (inesc[0] == ES_GOTESC) /* Don't write ESC yet */
2223 else if (oldesc[0] == ES_GOTESC && !apcactive) {
2224 ckcputc(ESC); /* Write saved ESC */
2225 if (seslog && !sessft) logchar((char)ESC);
2226 } else if (apcrc) { /* We have an APC */
2227 debug(F111,"CONNECT APC complete",apcbuf,apclength);
2228 ckcputf(); /* Force screen update */
2229 cx_status = CSX_APC;
2234 #endif /* NOESCSEQ */
2236 debug(F111,"INXBUF",inxbuf,inxcount);
2237 for (i = 0; i < inxcount; i++) { /* Loop thru */
2238 c = inxbuf[i]; /* input expansion buffer... */
2241 !apcactive && /* Don't display APC sequences */
2243 !printing /* or transparent print material */
2246 c &= cmdmsk; /* Apply command mask. */
2247 if (c == CR && tt_crd) { /* SET TERM CR-DISPLA CRLF? */
2248 ckcputc(c); /* Yes, output CR */
2249 if (seslog && !sessft) logchar((char)c);
2250 c = LF; /* and insert a linefeed */
2252 if (dontprint) { /* Do transparent printing. */
2257 ckcputc(c); /* Write character to screen */
2259 if (seslog && !sessft) /* Handle session log. */
2262 if (printing && !inesc[0]) {
2263 /* zchout() can't be used because */
2264 /* it's buffered differently. */
2266 zsoutx(ZMFILE,(char *)cbuf,1);
2271 /* Check for trigger string */
2272 if (tt_trigger[0]) {
2274 if ((i = autoexitchk((CHAR)c)) > -1) {
2275 makestr(&triggerval,tt_trigger[i]);
2276 ckcputf(); /* Force screen update */
2278 fflush(stdout); /* I mean really force it */
2279 #endif /* NOSETBUF */
2280 cx_status = CSX_TRIGGER;
2284 #endif /* CK_TRIGGER */
2289 if (FD_ISSET(scrnout, &out)) {
2290 FD_CLR(scrnout, &out);
2293 } /* End of big loop */
2294 conret1: /* Come here to succeed */
2296 conret0: /* Common exit point */
2300 closesocket(pair[0]);
2301 closesocket(pair[1]);
2302 x = kill(tid,SIGKILLTHR); /* Kill thread */
2303 wait_for_thread (tid, &ret_val);
2308 if (learning && learnfp)
2309 fputs("\n",learnfp);
2310 #endif /* CKLEARN */
2317 && !TELOPT_ME(TELOPT_COMPORT)
2321 #endif /* NETCONN */
2325 This is bad because if they said SET MODEM HANGUP-METHOD MODEM-COMMAND,
2326 they mean it -- we shouldn't fall back on tthang() if mdmhup() fails,
2327 because maybe they have some special kind of connection. On the other
2328 hand, making this change prevents dialing from working at all in some
2329 cases. Further study needed.
2332 if (dohangup > 1) /* User asked for it */
2333 if (mdmhup() < 1) /* Maybe hang up via modem */
2335 tthang(); /* And make sure we don't hang up */
2337 if (!network) { /* Serial connection. */
2339 if (dialmhu) /* Hang up the way they said to. */
2345 #endif /* COMMENT */
2347 dohangup = 0; /* again unless requested again. */
2349 if (quitnow) /* Exit now if requested. */
2350 doexit(GOOD_EXIT,xitsta);
2356 printf("(Back at %s)", *myhost ? myhost : "local UNIX system");
2361 what = W_NOTHING; /* So console modes set right. */
2363 language = langsv; /* Restore language */
2364 #endif /* NOCSETS */
2366 debug(F101,"CONNECT exit apcactive","",apcactive);
2367 debug(F101,"CONNECT exit justone","",justone);
2371 if (apcactive == APC_LOCAL)
2374 printf("----------------------------------------------------\n");
2382 /* H C O N N E -- Give help message for connect. */
2384 #define CXM_SER 1 /* Serial connections only */
2385 #define CXM_NET 2 /* Network only (but not Telnet) */
2386 #define CXM_TEL 4 /* Telnet only */
2388 static struct hmsgtab {
2392 {" ? or H for this message", 0},
2393 {" 0 (zero) to send the NUL (0) character", 0},
2394 {" B to send a BREAK signal (0.275sec)", CXM_SER},
2396 {" B to send a network BREAK", CXM_NET},
2397 {" B to send a Telnet BREAK", CXM_TEL},
2398 #endif /* NETCONN */
2400 {" L to send a Long BREAK (1.5sec)", CXM_SER},
2401 #endif /* CK_LBRK */
2403 {" I to send a network interrupt packet", CXM_NET},
2404 {" I to send a Telnet Interrupt request", CXM_TEL},
2406 {" A to send Telnet Are-You-There?", CXM_TEL},
2408 #endif /* NETCONN */
2409 {" U to hangup and close the connection", 0},
2410 {" Q to hangup and quit Kermit", 0},
2411 {" S for status", 0},
2413 {" ! to push to local shell (disabled)", 0},
2414 {" Z to suspend (disabled)", 0},
2416 {" ! to push to local shell", 0},
2418 {" Z to suspend (disabled)", 0},
2420 {" Z to suspend", 0},
2423 {" \\ backslash code:", 0},
2424 {" \\nnn decimal character code", 0},
2425 {" \\Onnn octal character code", 0},
2426 {" \\Xhh hexadecimal character code;", 0},
2427 {" terminate with Carriage Return.", 0},
2428 {" Type the escape character again to send the escape character itself,",
2430 {" or press the space-bar to resume the CONNECT session.", 0},
2438 cxtype = IS_TELNET() ? CXM_TEL : CXM_NET;
2442 conol("\r\n----------------------------------------------------\r\n");
2444 conol(" C to return to ");
2445 conoll(*myhost ? myhost : "the C-Kermit prompt");
2446 for (i = 0; hlpmsg[i].hmsg; i++) {
2447 if (!(hlpmsg[i].hflags) || (hlpmsg[i].hflags == cxtype))
2448 conoll(hlpmsg[i].hmsg);
2450 conol("Press a key>"); /* Prompt for command. */
2451 c = CONGKS() & 0177; /* Get character, strip any parity. */
2452 /* No key mapping or translation here */
2455 conoll("----------------------------------------------------");
2456 return(c); /* Return it. */
2460 /* D O E S C -- Process an escape character argument */
2467 #endif /* CK_ANSIC */
2471 debug(F101,"CONNECT doesc","",c);
2473 if (c == escape) { /* Send escape character */
2474 d = dopar((CHAR) c); ttoc((char) d); return;
2475 } else /* Or else look it up below. */
2476 if (isupper(c)) c = tolower(c);
2480 case 'c': /* Escape back to prompt */
2482 cx_status = CSX_ESCAPE;
2487 " WARNING: This version of C-Kermit has no command processor to escape"
2490 " back to. To return to your local system, log out from the remote and/or"
2493 " use the escape character followed by the letter U to close (hang Up) the"
2496 " connection. Resuming your session..."
2501 active = 0; conol("\r\n"); return;
2504 case 'b': /* Send a BREAK signal */
2508 #endif /* CKLEARN */
2512 case 'i': /* Send Interrupt */
2515 if (network && IS_TELNET()) { /* TELNET */
2516 temp[0] = (CHAR) IAC; /* I Am a Command */
2517 temp[1] = (CHAR) TN_IP; /* Interrupt Process */
2519 ttol((CHAR *)temp,2);
2521 #endif /* TCPSOCKET */
2526 case 'a': /* "Are You There?" */
2528 if (network && IS_TELNET()) {
2529 temp[0] = (CHAR) IAC; /* I Am a Command */
2530 temp[1] = (CHAR) TN_AYT; /* Are You There? */
2532 ttol((CHAR *)temp,2);
2535 #endif /* TCPSOCKET */
2536 #endif /* NETCONN */
2539 case 'l': /* Send a Long BREAK signal */
2542 #endif /* CKLEARN */
2544 #endif /* CK_LBRK */
2546 case 'u': /* Hangup */
2547 /* case '\010': */ /* No, too dangerous */
2548 cx_status = CSX_USERDISC;
2549 dohangup = 2; active = 0; conol("\r\nHanging up "); return;
2551 case 'q': /* Quit */
2552 cx_status = CSX_USERDISC;
2553 dohangup = 2; quitnow = 1; active = 0; conol("\r\n"); return;
2555 case 's': /* Status */
2557 conoll("----------------------------------------------------");
2560 ckmakmsg(temp,TMPLEN," Pipe: \"",ttname,"\"",NULL);
2562 ckmakmsg(temp,TMPLEN," Pty: \"",ttname,"\"",NULL);
2564 #endif /* PTYORPIPE */
2568 (network ? "Host" : "Device"),
2574 /* The following sprintf's are safe, temp[] size is at least 200 */
2576 if (!network && speed >= 0L) {
2577 sprintf(temp,"Speed %ld", speed);
2580 sprintf(temp," Terminal echo: %s", duplex ? "local" : "remote");
2582 sprintf(temp," Terminal bytesize: %d", (cmask == 0177) ? 7 : 8);
2584 sprintf(temp," Command bytesize: %d", (cmdmsk == 0177) ? 7 : 8);
2587 sprintf(temp," Parity[hardware]: %s",parnam(hwparity));
2589 sprintf(temp," Parity: %s", parnam(parity));
2592 sprintf(temp," Autodownload: %s", autodl ? "on" : "off");
2595 ckmakmsg(temp, /* (would not be safe for sprintf) */
2598 *sesfil ? sesfil : "(none)",
2604 if (!network) shomdm();
2611 sprintf(temp," Elapsed time: %s",hhmmss(z));
2615 #endif /* CKLOGDIAL */
2616 conoll("----------------------------------------------------");
2619 case 'h': /* Help */
2620 case '?': /* Help */
2621 c = hconne(); continue;
2623 case '0': /* Send a null */
2624 c = '\0'; d = dopar((CHAR) c); ttoc((char) d); return;
2626 case 'z': case '\032': /* Suspend */
2637 case '@': /* Start inferior command processor */
2641 conres(); /* Put console back to normal */
2642 zshcmd(""); /* Fork a shell. */
2643 if (conbin((char)escape) < 0) {
2644 cx_status = CSX_INTERNAL;
2645 printf("Error resuming CONNECT session\n");
2654 case SP: /* Space, ignore */
2657 default: /* Other */
2658 if (c == CMDQ) { /* Backslash escape */
2662 while (((c = (CONGKS() & cmdmsk)) != '\r') && (c != '\n'))
2664 *ecbp = NUL; ecbp = ecbuf;
2665 x = xxesc(&ecbp); /* Interpret it */
2666 if (x >= 0) { /* No key mapping here */
2667 c = dopar((CHAR) x);
2670 } else { /* Invalid backslash code. */
2675 conoc(BEL); return; /* Invalid esc arg, beep */
2679 #endif /* NOLOCAL */