| #include "ckcsym.h" |
| |
| char *connv = "CONNECT Command for UNIX:fork(), 8.0.114, 29 Nov 2002"; |
| |
| /* C K U C O N -- Terminal connection to remote system, for UNIX */ |
| /* |
| Author: Frank da Cruz <fdc@columbia.edu>, |
| Columbia University Academic Information Systems, New York City. |
| |
| Copyright (C) 1985, 2004, |
| 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. |
| |
| NOTE: This module has been superseded on most platforms by ckucns.c, which |
| uses select() rather than fork() for multiplexing its i/o. This module |
| is still needed for platforms that do not support select(), and also for |
| its X.25 support. Although the two modules share large amounts of code, |
| their structure is radically different and therefore attempts at merging |
| them have so far been unsuccessful. (November 1998.) |
| |
| Special thanks to Eduard Vopicka, Prague University of Economics, |
| Czech Republic, for valuable contributions to this module in July 1994, |
| and to Neal P. Murphy of the Motorola Cellular Infrastructure Group in 1996 |
| for rearranging the code to allow operation on the BeBox, yet still work |
| in regular UNIX. |
| */ |
| #include "ckcdeb.h" /* Common things first */ |
| |
| #ifndef NOLOCAL |
| |
| #ifdef BEOSORBEBOX |
| static double time_started = 0.0; |
| #include <kernel/OS.h> |
| _PROTOTYP( static long concld, (void *) ); |
| #else |
| _PROTOTYP( static VOID concld, (void) ); |
| #endif /* BEOSORBEBOX */ |
| |
| #ifdef NEXT |
| #undef NSIG |
| #include <sys/wait.h> /* For wait() */ |
| #endif /* NEXT */ |
| |
| #include <signal.h> /* Signals */ |
| #include <errno.h> /* Error numbers */ |
| |
| #ifdef ZILOG /* Longjumps */ |
| #include <setret.h> |
| #else |
| #include <setjmp.h> |
| #endif /* ZILOG */ |
| #include "ckcsig.h" |
| |
| /* Kermit-specific includes */ |
| |
| #include "ckcasc.h" /* ASCII characters */ |
| #include "ckcker.h" /* Kermit things */ |
| #include "ckucmd.h" /* For xxesc() prototype */ |
| #include "ckcnet.h" /* Network symbols */ |
| #ifndef NOCSETS |
| #include "ckcxla.h" /* Character set translation */ |
| #endif /* NOCSETS */ |
| |
| /* Internal function prototypes */ |
| |
| _PROTOTYP( VOID ttflux, (void) ); |
| _PROTOTYP( VOID doesc, (char) ); |
| _PROTOTYP( VOID logchar, (char) ); |
| _PROTOTYP( int hconne, (void) ); |
| #ifndef NOSHOW |
| _PROTOTYP( VOID shomdm, (void) ); |
| #endif /* NOSHOW */ |
| _PROTOTYP( static int kbget, (void) ); |
| _PROTOTYP( static int pipemsg, (int) ); |
| _PROTOTYP( static int ckcputf, (void) ); |
| _PROTOTYP( static VOID ck_sndmsg, (void) ); |
| /* |
| For inter-fork signaling. Normally we use SIGUSR1, except on SCO, where |
| we use SIGUSR2 because SIGUSR1 is used by the system. You can define |
| CK_FORK_SIG to be whatever other signal you might want to use at compile |
| time. We don't use SIGUSR2 everywhere because in some systems, like |
| UnixWare, the default action for SIGUSR2 is to kill the process that gets it. |
| */ |
| #ifndef CK_FORK_SIG |
| |
| #ifndef SIGUSR1 /* User-defined signals */ |
| #define SIGUSR1 30 |
| #endif /* SIGUSR1 */ |
| |
| #ifndef SIGUSR2 |
| #define SIGUSR2 31 |
| #endif /* SIGUSR2 */ |
| |
| #ifdef M_UNIX |
| #define CK_FORK_SIG SIGUSR2 /* SCO - use SIGUSR2 */ |
| #else |
| #define CK_FORK_SIG SIGUSR1 /* Others - use SIGUSR1 */ |
| #endif /* M_UNIX */ |
| |
| #endif /* CK_FORK_SIG */ |
| |
| /* External variables */ |
| |
| extern struct ck_p ptab[]; |
| |
| extern int local, escape, duplex, parity, flow, seslog, sessft, debses, |
| mdmtyp, ttnproto, cmask, cmdmsk, network, nettype, deblog, sosi, tnlm, |
| xitsta, what, ttyfd, ttpipe, quiet, backgrd, pflag, tt_crd, tn_nlm, ttfdflg, |
| tt_escape, justone, carrier, hwparity; |
| |
| extern long speed; |
| extern char ttname[], sesfil[], myhost[], *ccntab[]; |
| #ifdef TNCODE |
| extern int tn_b_nlm, tn_rem_echo; |
| #endif /* TNCODE */ |
| |
| #ifdef CK_TRIGGER |
| extern char * tt_trigger[], * triggerval; |
| #endif /* CK_TRIGGER */ |
| |
| extern int nopush; |
| |
| #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; |
| extern int protocol; /* Auto download */ |
| #endif /* CK_APC */ |
| |
| extern int autodl; |
| #ifdef CK_AUTODL |
| extern CHAR ksbuf[]; |
| #endif /* CK_AUTODL */ |
| |
| #ifdef CK_XYZ |
| #ifdef XYZ_INTERNAL |
| static int zmdlok = 1; /* Zmodem autodownloads available */ |
| #else |
| static int zmdlok = 0; /* Depends on external protocol def */ |
| #endif /* XYZ_INTERNAL */ |
| #else |
| static int zmdlok = 0; /* Not available at all */ |
| #endif /* CK_XYZ */ |
| |
| #ifndef NOSETKEY /* Keyboard mapping */ |
| extern KEY *keymap; /* Single-character key map */ |
| extern MACRO *macrotab; /* Key macro pointer table */ |
| static MACRO kmptr = NULL; /* Pointer to current key macro */ |
| #endif /* NOSETKEY */ |
| |
| /* Global variables local to this module */ |
| |
| static int |
| quitnow = 0, /* <esc-char>Q was typed */ |
| jbset = 0, /* Flag whether jmp buf is set. */ |
| dohangup = 0, /* <esc-char>H was typed */ |
| sjval, /* Setjump return value */ |
| goterr = 0, /* Fork/pipe creation error flag */ |
| inshift = 0, /* SO/SI shift states */ |
| outshift = 0; |
| |
| int active = 0; /* Lower fork active flag */ |
| |
| static PID_T parent_id = (PID_T)0; /* Process ID of keyboard fork */ |
| |
| static char ecbuf[10], *ecbp; /* Escape char buffer & pointer */ |
| |
| #ifdef CK_SMALL |
| #define IBUFL 1536 /* Input buffer length */ |
| #else |
| #define IBUFL 4096 |
| #endif /* CK_SMALL */ |
| |
| static int obc = 0; /* Output buffer count */ |
| |
| #ifndef OXOS |
| #define OBUFL 1024 /* Output buffer length */ |
| #else |
| #define OBUFL IBUFL |
| #endif /* OXOS */ |
| |
| #ifdef BIGBUFOK |
| #define TMPLEN 4096 /* Temporary message buffer length */ |
| #else |
| #define TMPLEN 200 |
| #endif /* BIGBUFOK */ |
| |
| #ifdef DYNAMIC |
| static char *ibuf = NULL, *obuf = NULL, *temp = NULL; /* Buffers */ |
| #else |
| static char ibuf[IBUFL], obuf[OBUFL], temp[TMPLEN]; |
| #endif /* DYNAMIC */ |
| |
| #ifdef DYNAMIC |
| static char *ibp; /* Input buffer pointer */ |
| #else |
| static char *ibp = ibuf; /* Input buffer pointer */ |
| #endif /*DYNAMIC */ |
| static int ibc = 0; /* Input buffer count */ |
| |
| #ifdef DYNAMIC |
| static char *obp; /* Output buffer pointer */ |
| #else |
| static char *obp = obuf; /* Output buffer pointer */ |
| #endif /* DYNAMIC */ |
| |
| /* X.25 items */ |
| |
| #ifdef ANYX25 |
| static char *p; /* General purpose pointer */ |
| char x25ibuf[MAXIX25]; /* Input buffer */ |
| char x25obuf[MAXOX25]; /* Output buffer */ |
| int ibufl; /* Length of input buffer */ |
| int obufl; /* Length of output buffer */ |
| unsigned char tosend = 0; |
| int linkid, lcn; |
| static int dox25clr = 0; |
| #ifndef IBMX25 |
| extern CHAR padparms[]; |
| #endif /* IBMX25 */ |
| #endif /* ANYX25 */ |
| |
| static int xpipe[2] = {-1, -1}; /* Pipe descriptor for child-parent messages */ |
| static PID_T pid = (PID_T) 0; /* Process ID of child */ |
| |
| /* Character-set items */ |
| |
| static int unicode = 0; |
| |
| static int |
| escseq = 0, /* 1 = Recognizer is active */ |
| inesc = 0, /* State of sequence recognizer */ |
| oldesc = -1; /* Previous state of recognizer */ |
| |
| #define OUTXBUFSIZ 15 |
| static CHAR inxbuf[OUTXBUFSIZ+1]; /* Host-to-screen expansion buffer */ |
| static int inxcount = 0; /* and count */ |
| static CHAR outxbuf[OUTXBUFSIZ+1]; /* Keyboard-to-host expansion buf */ |
| static int outxcount = 0; /* and count */ |
| |
| #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. */ |
| static int langsv; /* For remembering language setting. */ |
| extern struct csinfo fcsinfo[]; /* File character set info. */ |
| extern int tcsr, tcsl; /* Terminal character sets, remote & local. */ |
| static int tcs; /* Intermediate ("transfer") character set. */ |
| static int tcssize = 0; /* Size of tcs */ |
| #ifdef UNICODE /* UTF-8 support */ |
| #ifdef CK_ANSIC |
| extern int (*xl_ufc[MAXFCSETS+1])(USHORT); /* Unicode to FCS */ |
| extern USHORT (*xl_fcu[MAXFCSETS+1])(CHAR); /* FCS to Unicode */ |
| extern int (*xuf)(USHORT); /* Translation function UCS to FCS */ |
| extern USHORT (*xfu)(CHAR); /* Translation function FCS to UCS */ |
| #else |
| extern int (*xl_ufc[MAXFCSETS+1])(); |
| extern USHORT (*xl_fcu[MAXFCSETS+1])(); |
| extern int (*xuf)(); |
| extern USHORT (*xfu)(); |
| #endif /* CK_ANSIC */ |
| #endif /* UNICODE */ |
| #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 */ |
| |
| /* Child process events and messages */ |
| |
| #define CEV_NO 0 /* No event */ |
| #define CEV_HUP 1 /* Communications hangup */ |
| #define CEV_PAD 2 /* X.25 - change PAD parameters */ |
| #define CEV_DUP 3 /* Toggle duplex */ |
| #define CEV_APC 4 /* Execute APC */ |
| #ifdef TNCODE |
| #define CEV_MEBIN 5 /* Change of me_binary */ |
| #define CEV_UBIN 6 /* Change of u_binary */ |
| #endif /* TNCODE */ |
| #define CEV_ADL 7 /* Autodownload */ |
| #define CEV_AUL 8 /* Autoupload */ |
| #define CEV_TRI 9 /* Trigger string */ |
| |
| #ifdef NOESCSEQ |
| #define chkaes(x) 0 |
| #else |
| /* |
| As of edit 178, the CONNECT command skips past ANSI escape sequences to |
| avoid translating the characters within them. This allows the CONNECT |
| command to work correctly with a 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 */ |
| |
| /* |
| 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) |
| #else |
| chkaes(c) char c; |
| #endif /* CK_ANSIC */ |
| /* chkaes */ { |
| |
| oldesc = inesc; /* Remember previous state */ |
| if (c == CAN || c == SUB) /* CAN and SUB cancel any sequence */ |
| inesc = ES_NORMAL; |
| else /* Otherwise */ |
| switch (inesc) { /* enter state switcher */ |
| |
| case ES_NORMAL: /* NORMAL state */ |
| if (c == ESC) /* Got an ESC */ |
| 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 */ |
| inesc = ES_GOTCSI; /* Change to GOTCSI state */ |
| else if (c == 'P' || (c > 0134 && c < 0140)) { /* P, [, ^, or _ */ |
| inesc = ES_STRING; /* Switch to STRING-absorption state */ |
| #ifdef CK_APC |
| if (c == '_' && pid == 0 && /* APC handled in child only */ |
| (apcstatus & APC_ON)) { /* and only if not disabled. */ |
| debug(F100,"CONNECT APC begin","",0); |
| apcactive = APC_REMOTE; /* Set APC-Active flag */ |
| apclength = 0; /* and reset APC buffer pointer */ |
| } |
| #endif /* CK_APC */ |
| } else if (c > 057 && c < 0177) /* Final character '0' thru '~' */ |
| inesc = ES_NORMAL; /* Back to normal */ |
| else if (c != ESC) /* ESC in an escape sequence... */ |
| 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 '~' */ |
| inesc = ES_NORMAL; /* Return to NORMAL state. */ |
| else if (c == ESC) /* ESC ... */ |
| 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 '~' */ |
| inesc = ES_NORMAL; /* Return to NORMAL. */ |
| else if (c == ESC) /* ESC ... */ |
| 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 */ |
| 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 */ |
| 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 */ |
| inesc = ES_NORMAL; /* If so, back to NORMAL */ |
| #ifdef CK_APC |
| if (apcactive) { /* If it was an APC string, */ |
| debug(F101,"CONNECT APC terminated","",c); |
| apcbuf[apclength] = NUL; /* terminate it and then ... */ |
| return(1); |
| } |
| #endif /* CK_APC */ |
| } else { /* Otherwise */ |
| 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 */ |
| |
| /* Connect state parent/child communication signal handlers */ |
| |
| /* Routines used by the child process */ |
| |
| static int |
| pipemsg(n) int n; { /* Send message ID to parent */ |
| int code = n & 255; |
| return(write(xpipe[1], &code, sizeof(code))); |
| } |
| |
| /* Environment pointer for CK_FORK_SIG signal handling in child... */ |
| |
| #ifdef CK_POSIX_SIG |
| static sigjmp_buf sig_env; |
| #else |
| static jmp_buf sig_env; |
| #endif /* CK_POSIX_SIG */ |
| |
| static SIGTYP /* CK_FORK_SIG handling in child ... */ |
| forkint(foo) int foo; { |
| /* It is important to disable CK_FORK_SIG before longjmp */ |
| signal(CK_FORK_SIG, SIG_IGN); /* Set to ignore CK_FORK_SIG */ |
| debug(F100,"CONNECT forkint - CK_FORK_SIG", "", 0); |
| /* Force return from ck_sndmsg() */ |
| cklongjmp(sig_env, 1); |
| /* NOTREACHED */ |
| } |
| |
| static VOID |
| ck_sndmsg() { /* Executed by child only ... */ |
| debug(F100,"CONNECT ck_sndmsg, active", "", active); |
| if ( |
| #ifdef CK_POSIX_SIG |
| sigsetjmp(sig_env,1) |
| #else |
| setjmp(sig_env) |
| #endif /* CK_POSIX_SIG */ |
| == 0) { |
| debug(F100,"CONNECT ck_sndmsg signaling parent","",0); |
| signal(CK_FORK_SIG, forkint); /* Set up signal handler */ |
| kill(parent_id, CK_FORK_SIG); /* Kick the parent */ |
| debug(F100,"ck_sndmsg pausing","",0); |
| for (;;) pause(); /* Wait for CK_FORK_SIG or SIGKILL */ |
| /* NOTREACHED */ |
| } |
| /* We come here from forkint() via [sig]cklongjmp(sig_env,1) */ |
| debug(F100,"CONNECT ck_sndmsg is parent - returning", "", 0); |
| } |
| |
| /* Routines used by the parent process */ |
| |
| #ifdef CK_POSIX_SIG /* Environment pointer for CONNECT errors */ |
| static sigjmp_buf con_env; |
| #else |
| static jmp_buf con_env; |
| #endif /* CK_POSIX_SIG */ |
| /* |
| pipeint() handles CK_FORK_SIG signals from the lower (port) fork. |
| It reads a function code from the pipe that connects the two forks, |
| then reads additional data from the pipe, then handles it. |
| */ |
| static SIGTYP |
| pipeint(arg) int arg; { /* Dummy argument */ |
| int code, cx, x, i /* , n */ ; |
| |
| #ifndef NOCCTRAP |
| extern ckjmpbuf cmjbuf; |
| #endif /* NOCCTRAP */ |
| /* |
| IMPORTANT: At this point, the child fork is waiting for CK_FORK_SIG |
| (eventually for SIGKILL) inside of ck_sndmsg(). So we can't get any |
| subsequent CK_FORK_SIG from child before we send it CK_FORK_SIG. |
| */ |
| signal(CK_FORK_SIG, SIG_IGN); /* Ignore CK_FORK_SIG now */ |
| debug(F101,"CONNECT pipeint arg","",arg); |
| |
| read(xpipe[0], &code, sizeof(code)); /* Get function code from pipe */ |
| debug(F101,"CONNECT pipeint code","",code); |
| cx = code & 255; /* 8-bit version of function code */ |
| |
| #ifndef NOCCTRAP |
| #ifndef NOICP |
| #define USECCJMPBUF |
| #endif /* NOICP */ |
| #endif /* NOCCTRAP */ |
| /* |
| Read info passed back up to us by the lower fork, depending on the function |
| requested. The same number of items must be read from the pipe in the same |
| order as the lower fork put them there. Trying to read something that's not |
| there makes the program hang uninterruptibly. Pay close attention -- notice |
| how we fall through some of the cases rather than break; that's deliberate. |
| */ |
| switch (cx) { |
| #ifdef CK_TRIGGER |
| case CEV_TRI: /* Trigger string */ |
| debug(F100,"CONNECT trigger","",0); |
| read(xpipe[0], (char *)&i, sizeof(i)); /* Trigger index */ |
| debug(F101,"CONNECT trigger index","",i); |
| makestr(&triggerval,tt_trigger[i]); /* Make a copy of the trigger */ |
| debug(F110,"CONNECT triggerval",triggerval,0); |
| read(xpipe[0], (char *)&ibc, sizeof(ibc)); /* Copy child's */ |
| debug(F101,"CONNECT trigger ibc (upper)","",ibc); /* input buffer. */ |
| if (ibc > 0) { |
| read(xpipe[0], (char *)&ibp, sizeof(ibp)); |
| read(xpipe[0], ibp, ibc); |
| } |
| /* Fall thru... */ |
| |
| #endif /* CK_TRIGGER */ |
| |
| case CEV_HUP: |
| /* |
| The CEV_HUP case is executed when the other side has hung up on us. |
| In some cases, this happens before we have had a chance to execute the |
| setjmp(con_env,1) call, and in that case we'd better not take the longjmp! |
| A good example is when you TELNET to port 13 on the local host; it prints |
| its asctime() string (26 chars) and then closes the connection. |
| */ |
| #ifdef CK_TRIGGER |
| if (cx == CEV_TRI) |
| sjval = CEV_TRI; /* Set global variable. */ |
| else |
| #endif /* CK_TRIGGER */ |
| sjval = CEV_HUP; |
| if (jbset) { /* jmp_buf is initialized */ |
| cklongjmp(con_env,sjval); /* so do the right thing. */ |
| } else { |
| int x = 0; |
| #ifdef USECCJMPBUF |
| /* jmp_buf not init'd yet a close approximation... */ |
| #ifdef CK_TRIGGER |
| if (cx == CEV_HUP) |
| #endif /* CK_TRIGGER */ |
| ttclos(0); /* Close our end of the connection */ |
| if (pid) { |
| debug(F101,"CONNECT trigger killing pid","",pid); |
| #ifdef BEOSORBEBOX |
| { |
| long ret_val; |
| x = kill(pid,SIGKILLTHR); /* Kill lower fork */ |
| wait_for_thread (pid, &ret_val); |
| } |
| #else |
| #ifdef Plan9 |
| x = kill(pid, SIGKILL); /* (should always use this really) */ |
| #else |
| x = kill(pid,9); /* Kill lower fork (history) */ |
| #endif /* Plan9 */ |
| wait((WAIT_T *)0); /* Wait till gone. */ |
| if (x < 0) { |
| printf("ERROR: Failure to kill pid %ld: %s, errno=%d\n", |
| (long) pid, ck_errstr(), errno); |
| debug(F111,"CONNECT error killing stale pid", |
| ck_errstr(),errno); |
| } |
| pid = (PID_T) 0; |
| #endif /* BEOSORBEBOX */ |
| } |
| conres(); /* Reset the console. */ |
| if (!quiet) { |
| printf("\r\n(Back at %s)\r\n", |
| *myhost ? myhost : |
| #ifdef UNIX |
| "local UNIX system" |
| #else |
| "local system" |
| #endif /* UNIX */ |
| ); |
| } |
| what = W_NOTHING; /* So console modes are set right. */ |
| printf("\r\n"); /* prevent prompt-stomping */ |
| cklongjmp(cmjbuf,0); /* Do what the Ctrl-C handler does */ |
| #else |
| printf("\r\nLongjump failure - fatal\r\n"); |
| doexit(GOOD_EXIT,-1); /* Better than dumping core... */ |
| #endif /* USECCJMPBUF */ |
| } |
| #ifdef USECCJMPBUF |
| #undef USECCJMPBUF |
| #endif /* USECCJMPBUF */ |
| |
| case CEV_DUP: /* Child sends duplex change */ |
| read(xpipe[0], (char *)&duplex, sizeof(duplex)); |
| debug(F101,"CONNECT pipeint duplex","",duplex); |
| break; |
| #ifdef TNCODE |
| case CEV_MEBIN: /* Child sends me_binary change */ |
| read(xpipe[0], |
| (char *)&TELOPT_ME(TELOPT_BINARY), |
| sizeof(TELOPT_ME(TELOPT_BINARY)) |
| ); |
| debug(F101,"CONNECT pipeint me_binary","",TELOPT_ME(TELOPT_BINARY)); |
| break; |
| case CEV_UBIN: /* Child sends u_binary change */ |
| read(xpipe[0], |
| (char *)&TELOPT_U(TELOPT_BINARY), |
| sizeof(TELOPT_U(TELOPT_BINARY)) |
| ); |
| debug(F101,"CONNECT pipeint u_binary","",TELOPT_U(TELOPT_BINARY)); |
| break; |
| #endif /* TNCODE */ |
| |
| #ifdef CK_APC |
| case CEV_AUL: /* Autoupload */ |
| justone = 1; |
| debug(F100,"CONNECT autoupload at parent","",0); |
| #ifdef CK_AUTODL |
| case CEV_ADL: /* Autodownload */ |
| apcactive = APC_LOCAL; |
| if (!justone) debug(F100,"CONNECT autodownload at parent","",0); |
| /* Copy child's Kermit packet if any */ |
| read(xpipe[0], (char *)&x, sizeof(x)); |
| debug(F101,"CONNECT trigger ibc (upper)","",ibc); |
| if (x > 0) |
| read(xpipe[0], (char *)ksbuf, x+1); |
| #endif /* CK_AUTODL */ |
| case CEV_APC: /* Application Program Command */ |
| read(xpipe[0], (char *)&apclength, sizeof(apclength)); |
| read(xpipe[0], apcbuf, apclength+1); /* Include trailing zero byte */ |
| debug(F111,"CONNECT APC at parent",apcbuf,apclength); |
| read(xpipe[0], (char *)&ibc, sizeof(ibc)); /* Copy child's */ |
| if (ibc > 0) { /* input buffer. */ |
| read(xpipe[0], (char *)&ibp, sizeof(ibp)); |
| read(xpipe[0], ibp, ibc); |
| } |
| obc = 0; obp = obuf; *obuf = NUL; /* Because port fork flushed */ |
| sjval = CEV_APC; |
| cklongjmp(con_env,sjval); |
| /* NOTREACHED */ |
| #endif /* CK_APC */ |
| |
| #ifdef SUNX25 |
| case CEV_PAD: /* X.25 PAD parameter change */ |
| debug(F100,"CONNECT pipeint PAD change","",0); |
| read(xpipe[0],padparms,MAXPADPARMS); |
| sjval = CEV_PAD; /* Set global variable. */ |
| #ifdef COMMENT /* We might not need to do this... */ |
| cklongjmp(con_env,sjval); |
| /* NOTREACHED */ |
| #else /* COMMENT */ |
| break; |
| #endif /* COMMENT */ |
| #endif /* SUNX25 */ |
| } |
| signal(CK_FORK_SIG, pipeint); /* Set up signal handler */ |
| kill(pid, CK_FORK_SIG); /* Signal the port fork ... */ |
| } |
| |
| /* C K C P U T C -- C-Kermit CONNECT Put Character to Screen */ |
| /* |
| Output is buffered to avoid slow screen writes on fast connections. |
| NOTE: These could (easily?) become macros ... |
| */ |
| static int |
| ckcputf() { /* Dump the output buffer */ |
| int x = 0; |
| if (obc > 0) /* If we have any characters, */ |
| x = conxo(obc,obuf); /* dump them, */ |
| obp = obuf; /* reset the pointer */ |
| obc = 0; /* and the counter. */ |
| return(x); /* Return conxo's return code */ |
| } |
| |
| int |
| ckcputc(c) int c; { |
| int x; |
| |
| *obp++ = c & 0xff; /* Deposit the character */ |
| obc++; /* Count it */ |
| if (ibc == 0 || /* If input buffer about empty */ |
| obc == OBUFL) { /* or output buffer full */ |
| debug(F101,"CONNECT CKCPUTC obc","",obc); |
| x = conxo(obc,obuf); /* dump the buffer, */ |
| obp = obuf; /* reset the pointer */ |
| obc = 0; /* and the counter. */ |
| return(x); /* Return conxo's return code */ |
| } else return(0); |
| } |
| |
| /* C K C G E T C -- C-Kermit CONNECT Get Character */ |
| /* |
| Buffered read from communication device. |
| Returns the next character, refilling the buffer if necessary. |
| 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; { |
| int c, n; |
| #ifdef CK_SSL |
| extern int ssl_active_flag, tls_active_flag; |
| #endif /* CK_SSL */ |
| |
| #ifdef CK_ENCRYPTION |
| /* No buffering for possibly encrypted connections */ |
| if (network && IS_TELNET() && TELOPT_ME(TELOPT_AUTHENTICATION)) |
| return(ttinc(0)); |
| #endif /* CK_ENCRYPTION */ |
| #ifdef CK_SSL |
| if (ssl_active_flag || tls_active_flag) |
| return(ttinc(0)); |
| #endif /* CK_SSL */ |
| #ifdef COMMENT |
| /* too much */ |
| debug(F101,"CONNECT CKCGETC 1 ibc","",ibc); /* Log */ |
| #endif /* COMMENT */ |
| if (ibc < 1) { /* Need to refill buffer? */ |
| ibc = 0; /* Yes, reset count */ |
| ibp = ibuf; /* and buffer pointer */ |
| /* debug(F100,"CONNECT CKCGETC 1 calling ttinc(0)","",0); */ |
| #ifdef COMMENT |
| /* |
| This check is not worth the overhead. Scenario: ttchk() returns 0, so we |
| fall through to the blocking ttinc(). While in ttinc(), the connection is |
| lost. But the read() that ttinc() calls does not notice, and never returns. |
| This happens at least in HP-UX, and can be seen when we turn off the modem. |
| */ |
| if (!network && (carrier != CAR_OFF)) |
| if ((n = ttchk()) < 0) /* Make sure connection is not lost */ |
| return(n); |
| #endif /* COMMENT */ |
| c = ttinc(0); /* Read one character, blocking */ |
| /* debug(F101,"CONNECT CKCGETC 1 ttinc(0)","",c); */ |
| if (c < 0) { /* If error, return error code */ |
| return(c); |
| } else { /* Otherwise, got one character */ |
| *ibp++ = c; /* Advance buffer pointer */ |
| ibc++; /* and count. */ |
| } |
| if ((n = ttchk()) > 0) { /* Any more waiting? */ |
| if (n > (IBUFL - ibc)) /* Get them all at once. */ |
| n = IBUFL - ibc; /* Don't overflow buffer */ |
| if ((n = ttxin(n,(CHAR *)ibp)) > 0) { |
| #ifdef CK_ENCRYPTION |
| if (TELOPT_U(TELOPT_ENCRYPTION)) |
| ck_tn_decrypt(ibp,n); |
| #endif /* CK_ENCRYPTION */ |
| ibc += n; /* Advance counter */ |
| } |
| } else if (n < 0) { /* Error? */ |
| return(n); /* Return the error code */ |
| } |
| debug(F101,"CONNECT CKCGETC 2 ibc","",ibc); /* Log how many */ |
| ibp = ibuf; /* Point to beginning of buffer */ |
| } |
| c = *ibp++ & 0xff; /* Get next character from buffer */ |
| ibc--; /* Reduce buffer count */ |
| return(c); /* Return the character */ |
| } |
| |
| /* |
| Keyboard handling, buffered for speed, which is needed when C-Kermit is |
| in CONNECT mode between two other computers that are transferring data. |
| */ |
| static char *kbp; /* Keyboard input buffer pointer */ |
| static int kbc; /* Keyboard input buffer count */ |
| |
| #ifdef CK_SMALL /* Keyboard input buffer length */ |
| #define KBUFL 32 /* Small for PDP-11 UNIX */ |
| #else |
| #define KBUFL 257 /* Regular kernel size for others */ |
| #endif /* CK_SMALL */ |
| |
| #ifdef DYNAMIC |
| static char *kbuf = NULL; |
| #else |
| static char kbuf[KBUFL]; |
| #endif /* DYNAMIC */ |
| |
| /* Macro for reading keystrokes. */ |
| |
| #define CONGKS() (((--kbc)>=0) ? ((int)(*kbp++) & 0377) : kbget()) |
| |
| /* |
| Note that we call read() directly here, normally a no-no, but in this case |
| we know it's UNIX and we're only doing what coninc(0) would have done, |
| except we're reading a block of characters rather than just one. There is, |
| at present, no conxin() analog to ttxin() for chunk reads, and instituting |
| one would only add function-call overhead as it would only be a wrapper for |
| a read() call anyway. |
| */ |
| /* |
| Another note: We stick in this read() till the user types something. |
| But the other (lower) fork is running too, and on TELNET connections, |
| it will signal us to indicate echo-change negotiations, and this can |
| interrupt the read(). Some UNIXes automatically restart the interrupted |
| system call, others return from it with errno == EINTR. |
| */ |
| static int /* Keyboard buffer filler */ |
| kbget() { |
| #ifdef EINTR |
| int tries = 10; /* If read() is interrupted, */ |
| int ok = 0; |
| while (tries-- > 0) { /* try a few times... */ |
| #endif /* EINTR */ |
| if ((kbc = conchk()) < 1) /* How many chars waiting? */ |
| kbc = 1; /* If none or dunno, wait for one. */ |
| else if (kbc > KBUFL) /* If too many, */ |
| kbc = KBUFL; /* only read this many. */ |
| if ((kbc = read(0, kbuf, kbc)) < 1) { /* Now read it/them. */ |
| debug(F101,"CONNECT kbget errno","",errno); /* Got an error. */ |
| #ifdef EINTR |
| if (errno == EINTR) /* Interrupted system call. */ |
| continue; /* Try again, up to limit. */ |
| else /* Something else. */ |
| #endif /* EINTR */ |
| return(-1); /* Pass along read() error. */ |
| } |
| #ifdef EINTR |
| else { ok = 1; break; } |
| } |
| if (!ok) return(-1); |
| #endif /* EINTR */ |
| kbp = kbuf; /* Adjust buffer pointer, */ |
| kbc--; /* count, */ |
| return((int)(*kbp++) & 0377); /* and return first character. */ |
| } |
| |
| /* C O N C L D -- Interactive terminal connection child function */ |
| |
| static |
| #ifdef BEOSORBEBOX |
| long |
| #else |
| VOID |
| #endif /* BEOSORBEBOX */ |
| concld ( |
| #ifdef BEOSORBEBOX |
| void *bevoid |
| #endif /* BEOSORBEBOX */ |
| ) { |
| int n; /* General purpose counter */ |
| int i; /* For loops... */ |
| int c = -1; /* 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 */ |
| int prev; |
| #ifdef TNCODE |
| int tx; /* tn_doop() return code */ |
| #endif /* TNCODE */ |
| #ifdef CK_TRIGGER |
| int ix; /* Trigger index */ |
| #endif /* CK_TRIGGER */ |
| #ifndef NOESCSEQ |
| int apcrc; |
| #endif /* NOESCSEQ */ |
| |
| #ifdef COMMENT |
| int conret = 0; /* Return value from conect() */ |
| jbchksum = -1L; |
| #endif /* COMMENT */ |
| jbset = 0; /* jmp_buf not set yet, don't use it */ |
| debug(F101,"CONNECT concld entry","",CK_FORK_SIG); |
| /* *** */ /* Inferior reads, prints port input */ |
| |
| if (priv_can()) { /* Cancel all privs */ |
| printf("?setuid error - fatal\n"); |
| doexit(BAD_EXIT,-1); |
| } |
| signal(SIGINT, SIG_IGN); /* In case these haven't been */ |
| signal(SIGQUIT, SIG_IGN); /* inherited from above... */ |
| signal(CK_FORK_SIG, SIG_IGN); /* CK_FORK_SIG not expected yet */ |
| |
| inshift = outshift = 0; /* Initial SO/SI shift state. */ |
| { /* Wait for parent's setup */ |
| int i; |
| while ((i = read(xpipe[0], &c, 1)) <= 0) { |
| if (i < 0) { |
| debug(F101,"CONNECT concld setup error","",i); |
| debug(F111,"CONNECT concld setup error",ck_errstr(),errno); |
| pipemsg(CEV_HUP); /* Read error - hangup */ |
| ck_sndmsg(); /* Send and wait to be killed */ |
| /* NOTREACHED */ |
| } /* Restart interrupted read() */ |
| } |
| } |
| close(xpipe[0]); xpipe[0] = -1; /* Child - prevent future reads */ |
| #ifdef DEBUG |
| if (deblog) { |
| debug(F100,"CONNECT starting port fork","",0); |
| debug(F101,"CONNECT port fork ibc","",ibc); |
| debug(F101,"CONNECT port fork obc","",obc); |
| } |
| #endif /* DEBUG */ |
| what = W_CONNECT; |
| |
| while (1) { /* Fresh read, wait for a character. */ |
| #ifdef ANYX25 |
| if (network && (nettype == NET_SX25)) { |
| bzero(x25ibuf,sizeof(x25ibuf)) ; |
| if ((ibufl = ttxin(MAXIX25,(CHAR *)x25ibuf)) < 0) { |
| #ifndef IBMX25 |
| if (ibufl == -2) { /* PAD parms changes */ |
| pipemsg(CEV_PAD); |
| write(xpipe[1],padparms,MAXPADPARMS); |
| ck_sndmsg(); |
| } else { |
| #endif /* IBMX25 */ |
| if (!quiet) |
| printf("\r\nCommunications disconnect "); |
| dologend(); |
| pipemsg(CEV_HUP); |
| ck_sndmsg(); /* Wait to be killed */ |
| /* NOTREACHED */ |
| #ifndef IBMX25 |
| } |
| #endif /* IBMX25 */ |
| /* pause(); <--- SHOULD BE OBSOLETE NOW! */ |
| /* BECAUSE pause() is done inside of ck_sndmsg() */ |
| } |
| if (debses) { /* Debugging output */ |
| p = x25ibuf ; |
| while (ibufl--) { c = *p++; conol(dbchr(c)); } |
| } else { |
| if (seslog && sessft) /* Binary session log */ |
| logchar((char)c); /* Log char before translation */ |
| |
| if (sosi |
| #ifndef NOCSETS |
| || tcsl != tcsr |
| #endif /* NOCSETS */ |
| ) { /* Character at a time */ |
| for (i = 1; i < ibufl; i++) { |
| c = x25ibuf[i] & cmask; |
| if (sosi) { /* Handle SI/SO */ |
| if (c == SO) { |
| inshift = 1; |
| continue; |
| } else if (c == SI) { |
| inshift = 0; |
| continue; |
| } |
| if (inshift) |
| c |= 0200; |
| } |
| #ifndef NOCSETS |
| if (inesc == ES_NORMAL) { |
| #ifdef UNICODE |
| int x; |
| if (unicode == 1) { /* Remote is UTF-8 */ |
| x = u_to_b((CHAR)c); |
| if (x == -1) |
| continue; |
| else if (x == -2) { /* LS or PS */ |
| inxbuf[0] = CR; |
| inxbuf[1] = LF; |
| inxcount = 2; |
| } else { |
| inxbuf[0] = (unsigned)(x & 0xff); |
| } |
| c = inxbuf[0]; |
| } else if (unicode == 2) { /* Local is UTF-8 */ |
| inxcount = |
| b_to_u((CHAR)c,inxbuf,OUTXBUFSIZ,tcssize); |
| c = inxbuf[0]; |
| } else { |
| #endif /* UNICODE */ |
| if (sxi) c = (*sxi)((CHAR)c); |
| if (rxi) c = (*rxi)((CHAR)c); |
| inxbuf[0] = c; |
| #ifdef UNICODE |
| } |
| #endif /* UNICODE */ |
| } |
| #endif /* NOCSETS */ |
| c &= cmdmsk; /* Apply command mask. */ |
| conoc(c); /* Output to screen */ |
| if (seslog && !sessft) /* and session log */ |
| logchar(c); |
| } |
| } else { /* All at once */ |
| for (i = 1; i < ibufl; i++) |
| x25ibuf[i] &= (cmask & cmdmsk); |
| conxo(ibufl,x25ibuf); |
| if (seslog) zsoutx(ZSFILE,x25ibuf,ibufl); |
| } |
| } |
| continue; |
| |
| } else { /* Not X.25... */ |
| #endif /* ANYX25 */ |
| /* |
| Get the next communication line character from our internal buffer. |
| If the buffer is empty, refill it. |
| */ |
| prev = c; /* Remember previous character */ |
| c = ckcgetc(0); /* Get next character */ |
| /* debug(F101,"CONNECT c","",c); */ |
| if (c < 0) { /* Failed... */ |
| debug(F101,"CONNECT disconnect ibc","",ibc); |
| debug(F101,"CONNECT disconnect obc","",obc); |
| ckcputf(); /* Flush CONNECT output buffer */ |
| if (!quiet) { |
| printf("\r\nCommunications disconnect "); |
| #ifdef COMMENT |
| if ( c == -3 |
| #ifdef ultrix |
| /* This happens on Ultrix if there's no carrier */ |
| && errno != EIO |
| #endif /* ultrix */ |
| #ifdef UTEK |
| /* This happens on UTEK if there's no carrier */ |
| && errno != EWOULDBLOCK |
| #endif /* UTEK */ |
| ) |
| perror("\r\nCan't read character"); |
| #endif /* COMMENT */ |
| } |
| #ifdef NOSETBUF |
| fflush(stdout); |
| #endif /* NOSETBUF */ |
| tthang(); /* Hang up the connection */ |
| debug(F111,"CONNECT concld i/o error",ck_errstr(),errno); |
| pipemsg(CEV_HUP); |
| ck_sndmsg(); /* Wait to be killed */ |
| } |
| #ifdef COMMENT |
| /* too much... */ |
| debug(F101,"CONNECT ** PORT","",c); /* Got character c OK. */ |
| #endif /* COMMENT */ |
| #ifdef TNCODE |
| /* Handle TELNET negotiations... */ |
| |
| if ((c == NUL) && network && IS_TELNET()) { |
| if (prev == CR) /* Discard <NUL> of <CR><NUL> */ |
| if (!TELOPT_U(TELOPT_BINARY)) |
| continue; |
| } |
| if ((c == IAC) && network && IS_TELNET()) { |
| int me_bin = TELOPT_ME(TELOPT_BINARY); |
| int u_bin = TELOPT_U(TELOPT_BINARY); |
| debug(F100,"CONNECT got IAC","",0); |
| ckcputf(); /* Dump screen-output buffer */ |
| if ((tx = tn_doop((CHAR)(c & 0xff),duplex,ckcgetc)) == 0) { |
| if (me_bin != TELOPT_ME(TELOPT_BINARY)) { |
| debug(F101, |
| "CONNECT TELNET me_bin", |
| "", |
| TELOPT_ME(TELOPT_BINARY) |
| ); |
| pipemsg(CEV_MEBIN); /* Tell parent */ |
| write(xpipe[1], |
| &TELOPT_ME(TELOPT_BINARY), |
| sizeof(TELOPT_ME(TELOPT_BINARY)) |
| ); |
| ck_sndmsg(); /* Tell the parent fork */ |
| } else if (u_bin != TELOPT_U(TELOPT_BINARY)) { |
| debug(F101, |
| "CONNECT TELNET u_bin", |
| "", |
| TELOPT_U(TELOPT_BINARY) |
| ); |
| pipemsg(CEV_UBIN); /* Tell parent */ |
| write(xpipe[1], |
| &TELOPT_U(TELOPT_BINARY), |
| sizeof(TELOPT_U(TELOPT_BINARY)) |
| ); |
| ck_sndmsg(); /* Tell the parent fork */ |
| } |
| continue; |
| } else if (tx == -1) { /* I/O error */ |
| if (!quiet) |
| printf("\r\nCommunications disconnect "); |
| #ifdef NOSETBUF |
| fflush(stdout); |
| #endif /* NOSETBUF */ |
| dologend(); |
| debug(F111,"CONNECT concld i/o error 2",ck_errstr(),errno); |
| pipemsg(CEV_HUP); |
| ck_sndmsg(); /* Wait to be killed */ |
| /* NOTREACHED */ |
| } else if (tx == -3) { /* I/O error */ |
| if (!quiet) |
| printf("\r\nConnection closed due to telnet policy "); |
| #ifdef NOSETBUF |
| fflush(stdout); |
| #endif /* NOSETBUF */ |
| dologend(); |
| debug(F111,"CONNECT concld i/o error 2",ck_errstr(),errno); |
| pipemsg(CEV_HUP); |
| ck_sndmsg(); /* Wait to be killed */ |
| /* NOTREACHED */ |
| } else if (tx == -2) { /* I/O error */ |
| if (!quiet) |
| printf("\r\nConnection closed by peer "); |
| #ifdef NOSETBUF |
| fflush(stdout); |
| #endif /* NOSETBUF */ |
| dologend(); |
| debug(F111,"CONNECT concld i/o error 2",ck_errstr(),errno); |
| pipemsg(CEV_HUP); |
| ck_sndmsg(); /* Wait to be killed */ |
| /* NOTREACHED */ |
| } else if ((tx == 1) && (!duplex)) { /* ECHO change */ |
| duplex = 1; /* Turn on local echo */ |
| debug(F101,"CONNECT TELNET duplex change","",duplex); |
| pipemsg(CEV_DUP); /* Tell parent */ |
| write(xpipe[1], &duplex, sizeof(duplex)); |
| ck_sndmsg(); /* Tell the parent fork */ |
| continue; |
| } else if ((tx == 2) && (duplex)) { /* ECHO change */ |
| duplex = 0; |
| debug(F101,"CONNECT TELNET duplex change","",duplex); |
| pipemsg(CEV_DUP); |
| write(xpipe[1], &duplex, sizeof(duplex)); |
| ck_sndmsg(); |
| continue; |
| } else if (tx == 3) { /* Quoted IAC */ |
| c = parity ? 127 : 255; |
| } |
| #ifdef IKS_OPTION |
| else if (tx == 4) { /* IKS State Change */ |
| if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start && |
| !tcp_incoming |
| ) { |
| /* here we need to print a msg that the other */ |
| /* side is in SERVER mode and that REMOTE */ |
| /* commands should be used. And CONNECT mode */ |
| /* should be ended. */ |
| active = 0; |
| } |
| } |
| #endif /* IKS_OPTION */ |
| else if (tx == 6) { |
| /* DO LOGOUT received */ |
| if (!quiet) |
| printf("\r\nRemote Logout "); |
| #ifdef NOSETBUF |
| fflush(stdout); |
| #endif /* NOSETBUF */ |
| debug(F100,"CONNECT Remote Logout","",0); |
| pipemsg(CEV_HUP); |
| ck_sndmsg(); /* Wait to be killed */ |
| /* NOTREACHED */ |
| } else |
| continue; /* Negotiation OK, get next char. */ |
| |
| } else if (parity) |
| c &= 0x7f; |
| |
| if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo) |
| ttoc(c); /* I'm echoing for the remote */ |
| #endif /* TNCODE */ |
| |
| if (debses) { /* Output character to screen */ |
| char *s; /* Debugging display... */ |
| s = dbchr(c); |
| while (*s) |
| ckcputc(*s++); |
| } else { /* Regular display ... */ |
| c &= cmask; /* Apply Kermit-to-remote mask */ |
| #ifdef CK_AUTODL |
| /* |
| Autodownload. Check for Kermit S packet prior to translation, since that |
| can change the packet and make it unrecognizable (as when the terminal |
| character set is an ISO 646 one)... Ditto for Zmodem start packet. |
| */ |
| if (autodl /* Autodownload enabled? */ |
| #ifdef IKS_OPTION |
| || TELOPT_SB(TELOPT_KERMIT).kermit.me_start |
| #endif /* IKS_OPTION */ |
| ) { |
| int k; |
| k = kstart((CHAR)c); /* Kermit S or I packet? */ |
| #ifdef CK_XYZ |
| if (!k && zmdlok) /* Or an "sz" start? */ |
| k = zstart((CHAR)c); |
| #endif /* CK_XYZ */ |
| if (k) { |
| int ksign = 0; |
| debug(F101,"CONNECT autodownload k","",k); |
| if (k < 0) { /* Minus-Protocol? */ |
| #ifdef NOSERVER |
| goto noserver; /* Need server mode for this */ |
| #else |
| ksign = 1; /* Remember */ |
| k = 0 - k; /* Convert to actual protocol */ |
| justone = 1; /* Flag for protocol module */ |
| #endif /* NOSERVER */ |
| } else |
| justone = 0; |
| k--; /* Adjust [kz]start's return value */ |
| if (k == PROTO_K |
| #ifdef CK_XYZ |
| || k == PROTO_Z |
| #endif /* CK_XYZ */ |
| ) { |
| |
| /* Now damage the packet so that it does not */ |
| /* trigger autodownload detection on subsquent */ |
| /* links. */ |
| |
| if (k == PROTO_K) { |
| int i, len = strlen((char*)ksbuf); |
| for (i = 0; i < len; i++) |
| ckcputc(BS); |
| } |
| #ifdef CK_XYZ |
| else { |
| int i; |
| for (i = 0; i < 3; i++) |
| ckcputc(CAN); |
| } |
| #endif /* CK_XYZ */ |
| /* Notify parent */ |
| pipemsg(justone ? CEV_AUL : CEV_ADL); |
| /* |
| Send our memory back up to the top fork thru the pipe. |
| CAREFUL -- Write this stuff in the same order it is to be read! |
| */ |
| /* Copy our Kermit packet to the parent fork */ |
| n = (int) strlen((char *)ksbuf); |
| write(xpipe[1], (char *)&n, sizeof(n)); |
| if (n > 0) |
| write(xpipe[1], (char *)ksbuf, n+1); |
| debug(F111,"CONNECT autodownload ksbuf",ksbuf,n); |
| debug(F101,"CONNECT autodownload justone","", |
| justone); |
| /* Construct the APC command */ |
| sprintf(apcbuf, |
| "set proto %s, %s, set proto %s", |
| ptab[k].p_name, |
| ksign ? "server" : "receive", |
| ptab[protocol].p_name |
| ); |
| apclength = strlen(apcbuf); |
| debug(F111,"CONNECT ksbuf",ksbuf,k); |
| debug(F110,"CONNECT autodownload",apcbuf,0); |
| apcactive = APC_LOCAL; |
| ckcputf(); /* Force screen update */ |
| |
| /* Write buffer including trailing NUL byte */ |
| debug(F101,"CONNECT write xpipe apclength","", |
| apclength); |
| write(xpipe[1], |
| (char *)&apclength, |
| sizeof(apclength) |
| ); |
| debug(F110,"CONNECT write xpipe apcbuf",apcbuf,0); |
| write(xpipe[1], apcbuf, apclength+1); |
| |
| /* Copy our input buffer to the parent fork */ |
| |
| debug(F101,"CONNECT autodownload complete ibc", |
| "",ibc); |
| debug(F101,"CONNECT autodownload complete obc", |
| "",obc); |
| write(xpipe[1], (char *)&ibc, sizeof(ibc)); |
| if (ibc > 0) { |
| write(xpipe[1], (char *)&ibp, sizeof(ibp)); |
| write(xpipe[1], ibp, ibc); |
| } |
| ck_sndmsg(); /* Wait to be killed */ |
| /* NOTREACHED */ |
| } |
| } |
| } |
| #ifdef NOSERVER |
| noserver: |
| #endif /* NOSERVER */ |
| #endif /* CK_AUTODL */ |
| if (sosi) { /* Handle SI/SO */ |
| if (c == SO) { /* Shift Out */ |
| inshift = 1; |
| continue; |
| } else if (c == SI) { /* Shift In */ |
| inshift = 0; |
| continue; |
| } |
| if (inshift) c |= 0200; |
| } |
| inxbuf[0] = c; /* In case there is no translation */ |
| inxcount = 1; /* ... */ |
| #ifndef NOCSETS |
| if (inesc == ES_NORMAL) { /* If not in an escape sequence */ |
| #ifdef UNICODE |
| int x; /* Translate character sets */ |
| CHAR ch; |
| ch = c; |
| if (unicode == 1) { /* Remote is UTF-8 */ |
| x = u_to_b(ch); |
| if (x < 0) |
| continue; |
| inxbuf[0] = (unsigned)(x & 0xff); |
| c = inxbuf[0]; |
| } else if (unicode == 2) { /* Local is UTF-8 */ |
| inxcount = b_to_u(ch,inxbuf,OUTXBUFSIZ,tcssize); |
| c = inxbuf[0]; |
| } else { |
| #endif /* UNICODE */ |
| if (sxi) c = (*sxi)((CHAR)c); |
| if (rxi) c = (*rxi)((CHAR)c); |
| inxbuf[0] = c; |
| #ifdef UNICODE |
| } |
| #endif /* UNICODE */ |
| } |
| #endif /* NOCSETS */ |
| |
| #ifndef NOESCSEQ |
| if (escseq) /* If handling escape sequences */ |
| apcrc = chkaes((char)c); /* update our state */ |
| #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_ON)) { |
| if (inesc == ES_GOTESC) /* Don't write ESC yet */ |
| continue; |
| else if (oldesc == ES_GOTESC && !apcactive) { |
| ckcputc(ESC); /* Write saved ESC */ |
| if (seslog && !sessft) |
| logchar((char)ESC); |
| } else if (apcrc) { /* We have an APC */ |
| debug(F111,"CONNECT APC complete",apcbuf,apclength); |
| ckcputf(); /* Force screen update */ |
| pipemsg(CEV_APC); /* Notify parent */ |
| write(xpipe[1], |
| (char *)&apclength, |
| sizeof(apclength) |
| ); |
| /* Write buffer including trailing NUL byte */ |
| |
| write(xpipe[1], apcbuf, apclength+1); |
| |
| /* Copy our input buffer to the parent fork */ |
| |
| debug(F101,"CONNECT APC complete ibc","",ibc); |
| debug(F101,"CONNECT APC complete obc","",obc); |
| write(xpipe[1], (char *)&ibc, sizeof(ibc)); |
| if (ibc > 0) { |
| write(xpipe[1], (char *)&ibp, sizeof(ibp)); |
| write(xpipe[1], ibp, ibc); |
| } |
| ck_sndmsg(); /* Wait to be killed */ |
| /* NOTREACHED */ |
| } |
| } |
| #endif /* CK_APC */ |
| #endif /* NOESCSEQ */ |
| |
| for (i = 0; i < inxcount; i++) { /* Loop thru */ |
| c = inxbuf[i]; /* input expansion buffer... */ |
| if ( |
| #ifdef CK_APC |
| !apcactive /* Ignore APC sequences */ |
| #else |
| 1 |
| #endif /* CK_APC */ |
| ) { |
| c &= cmdmsk; /* Apply command mask. */ |
| if (c == CR && tt_crd) { /* SET TERM CR-DISPLA CRLF? */ |
| ckcputc(c); /* Yes, output CR */ |
| if (seslog && !sessft) |
| logchar((char)c); |
| c = LF; /* and insert a linefeed */ |
| } |
| ckcputc(c); /* Write character to screen */ |
| } |
| if (seslog && !sessft) /* Handle session log */ |
| logchar((char)c); |
| #ifdef CK_TRIGGER |
| /* Check for trigger string */ |
| if (tt_trigger[0]) if ((ix = autoexitchk((CHAR)c)) > -1) { |
| ckcputf(); /* Force screen update */ |
| #ifdef NOSETBUF |
| fflush(stdout); /* I mean really force it */ |
| #endif /* NOSETBUF */ |
| pipemsg(CEV_TRI); /* Send up trigger indication */ |
| write(xpipe[1], (char *)&ix, sizeof(ix)); /* index */ |
| write(xpipe[1], (char *)&ibc, sizeof(ibc)); |
| if (ibc > 0) { |
| write(xpipe[1], (char *)&ibp, sizeof(ibp)); |
| write(xpipe[1], ibp, ibc); |
| } |
| debug(F100,"CONNECT concld trigger","",0); |
| ck_sndmsg(); /* Wait to be killed */ |
| active = 0; /* Shouldn't be necessary... */ |
| break; |
| } |
| /* NOTREACHED */ |
| #endif /* CK_TRIGGER */ |
| } |
| } |
| #ifdef ANYX25 |
| } |
| #endif /* ANYX25 */ |
| } |
| } |
| |
| |
| /* C O N E C T -- Interactive terminal connection */ |
| |
| int |
| conect() { |
| int n; /* General purpose counter */ |
| int i; /* For loops... */ |
| 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 */ |
| int c2; /* A copy of c */ |
| int csave; /* Another copy of c */ |
| #ifndef NOESCSEQ |
| int apcrc; |
| #endif /* NOESCSEQ */ |
| |
| int conret = 0; /* Return value from conect() */ |
| int msgflg = 0; |
| /* jbchksum = -1L; */ |
| jbset = 0; /* jmp_buf not set yet, don't use it */ |
| debok = 1; |
| |
| debug(F101,"CONNECT fork signal","",CK_FORK_SIG); |
| debug(F101,"CONNECT entry pid","",pid); |
| |
| msgflg = !quiet |
| #ifdef CK_APC |
| && !apcactive |
| #endif /* CK_APC */ |
| ; |
| /* |
| The following is to handle a fork left behind if we exit CONNECT mode |
| without killing it, and then return to CONNECT mode. This happened in |
| HP-UX, where the Reset key would raise SIGINT even though SIGINT was set to |
| SIG_IGN. The code below fixes the symptom; the real fix is in the main |
| SIGINT handler (if SIGINT shows up during CONNECT, just return rather than |
| taking the longjmp). |
| */ |
| if (pid) { /* This should be 0 */ |
| int x = 0; |
| debug(F101,"CONNECT entry killing stale pid","",pid); |
| printf("WARNING: Old CONNECT fork seems to be active.\n"); |
| printf("Attempting to remove it..."); |
| #ifdef BEOSORBEBOX |
| { |
| long ret_val; |
| x = kill(pid,SIGKILLTHR); /* Kill lower fork */ |
| wait_for_thread (pid, &ret_val); |
| } |
| #else |
| #ifdef Plan9 |
| x = kill(pid,SIGKILL); /* Kill lower fork */ |
| #else |
| x = kill(pid,9); |
| #endif /* Plan9 */ |
| #endif /* BEOSORBEBOX */ |
| wait((WAIT_T *)0); /* Wait till gone. */ |
| if (x < 0) { |
| printf("ERROR: Failure to kill pid %d: %s, errno=%d\n", |
| (int) pid, ck_errstr(), errno); |
| debug(F111,"CONNECT error killing stale pid",ck_errstr(),pid); |
| } |
| pid = (PID_T) 0; |
| printf("\n"); |
| } |
| signal(CK_FORK_SIG, SIG_IGN); /* Initial CK_FORK_SIG handling, */ |
| /* |
| The following ttimoff() call should not be necessary, but evidently there |
| are cases where a timer is left active and then goes off, taking a longjmp |
| to nowhere after the program's stack has changed. In any case, this is |
| safe because the CONNECT module uses no timer of any kind, and no other timer |
| should be armed while Kermit is in CONNECT mode. |
| */ |
| ttimoff(); /* Turn off any timer interrupts */ |
| |
| #ifdef CK_TRIGGER |
| makestr(&triggerval,NULL); /* Reset trigger */ |
| #endif /* CK_TRIGGER */ |
| |
| 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 */ |
| goto conret0; |
| } |
| if (speed < 0L && network == 0 && ttfdflg == 0) { |
| printf("Sorry, you must SET SPEED first\n"); |
| goto conret0; |
| } |
| #ifdef TCPSOCKET |
| if (network && (nettype != NET_TCPB) |
| #ifdef SUNX25 |
| && (nettype != NET_SX25) |
| #endif /* SUNX25 */ |
| #ifdef IBMX25 |
| && (nettype != NET_IX25) |
| #endif /* IBMX25 */ |
| #ifdef NETCMD |
| && (nettype != NET_CMD) |
| #endif /* NETCMD */ |
| #ifdef NETPTY |
| && (nettype != NET_PTY) |
| #endif /* NETPTY */ |
| ) { |
| printf("Sorry, network type not supported\n"); |
| goto conret0; |
| } |
| #endif /* TCPSOCKET */ |
| |
| #ifdef DYNAMIC |
| if (!ibuf) { |
| if (!(ibuf = malloc(IBUFL+1))) { /* Allocate input line buffer */ |
| printf("Sorry, CONNECT input buffer can't be allocated\n"); |
| goto conret0; |
| } else { |
| ibp = ibuf; |
| ibc = 0; |
| } |
| } |
| if (!obuf) { |
| if (!(obuf = malloc(OBUFL+1))) { /* Allocate output line buffer */ |
| printf("Sorry, CONNECT output buffer can't be allocated\n"); |
| goto conret0; |
| } else { |
| obp = obuf; |
| obc = 0; |
| } |
| } |
| if (!kbuf) { |
| if (!(kbuf = malloc(KBUFL+1))) { /* Allocate keyboard input buffer */ |
| printf("Sorry, CONNECT keyboard buffer can't be allocated\n"); |
| goto conret0; |
| } |
| } |
| if (!temp) { |
| if (!(temp = malloc(TMPLEN+1))) { /* Allocate temporary buffer */ |
| printf("Sorry, CONNECT temporary buffer can't be allocated\n"); |
| goto conret0; |
| } |
| } |
| #else |
| #ifdef COMMENT |
| ibp = ibuf; |
| ibc = 0; |
| #endif /* COMMENT */ |
| obp = obuf; |
| obc = 0; |
| #endif /* DYNAMIC */ |
| |
| kbp = kbuf; /* Always clear these. */ |
| *kbp = NUL; /* No need to preserve them between */ |
| kbc = 0; /* CONNECT sessions. */ |
| |
| #ifdef DEBUG |
| if (deblog) { |
| debug(F101,"CONNECT conect entry ttyfd","",ttyfd); |
| debug(F101,"CONNECT conect entry ibc","",ibc); |
| debug(F101,"CONNECT conect entry obc","",obc); |
| debug(F101,"CONNECT conect entry kbc","",kbc); |
| #ifdef CK_TRIGGER |
| debug(F110,"CONNECT conect trigger",tt_trigger[0],0); |
| #endif /* CK_TRIGGER */ |
| if (ttyfd > -1) { |
| n = ttchk(); |
| debug(F101,"CONNECT conect entry ttchk","",n); |
| } |
| } |
| #endif /* DEBUG */ |
| |
| if (ttyfd < 0) { /* If communication device not open */ |
| debug(F101,"CONNECT ttnproto","",ttnproto); |
| debug(F111,"CONNECT opening",ttname,0); /* Open it now */ |
| if (ttopen(ttname, |
| &local, |
| network ? -nettype : mdmtyp, |
| 0 |
| ) < 0) { |
| ckmakmsg(temp,TMPLEN,"Sorry, can't open ",ttname,NULL,NULL); |
| perror(temp); |
| debug(F110,"CONNECT open failure",ttname,0); |
| goto conret0; |
| } |
| #ifdef IKS_OPTION |
| /* If peer is in Kermit server mode, return now. */ |
| if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start) |
| return(0); |
| #endif /* IKS_OPTION */ |
| } |
| dohangup = 0; /* Hangup not requested yet */ |
| #ifdef ANYX25 |
| dox25clr = 0; /* X.25 clear not requested yet */ |
| #endif /* ANYX25 */ |
| |
| if (msgflg) { |
| #ifdef NETCONN |
| if (network) { |
| if (ttpipe) |
| printf("Connecting via command \"%s\"",ttname); |
| else |
| printf("Connecting to host %s",ttname); |
| #ifdef ANYX25 |
| if (nettype == NET_SX25 || nettype == NET_IX25) |
| printf(", Link ID %d, LCN %d",linkid,lcn); |
| #endif /* ANYX25 */ |
| } else { |
| #endif /* NETCONN */ |
| printf("Connecting to %s",ttname); |
| if (speed > -1L) printf(", speed %ld",speed); |
| #ifdef NETCONN |
| } |
| #endif /* NETCONN */ |
| if (tt_escape) { |
| printf("\r\n"); |
| shoesc(escape); |
| printf("Type the escape character followed by C to get back,\r\n"); |
| printf("or followed by ? to see other options.\r\n"); |
| } else { |
| printf(".\r\n\nESCAPE CHARACTER IS DISABLED\r\n\n"); |
| } |
| if (seslog) { |
| extern int slogts; |
| char * s = ""; |
| switch (sessft) { |
| case XYFT_D: |
| s = "debug"; break; |
| case XYFT_T: |
| s = slogts ? "timestamped-text" : "text"; break; |
| default: |
| s = "binary"; |
| } |
| printf("Session Log: %s, %s\r\n",sesfil,s); |
| } |
| if (debses) printf("Debugging Display...)\r\n"); |
| printf("----------------------------------------------------\r\n"); |
| fflush(stdout); |
| } |
| |
| /* Condition console terminal and communication line */ |
| |
| if (conbin((char)escape) < 0) { |
| printf("Sorry, can't condition console terminal\n"); |
| goto conret0; |
| } |
| debug(F101,"CONNECT cmask","",cmask); |
| debug(F101,"CONNECT cmdmsk","",cmdmsk); |
| debug(F101,"CONNECT speed before ttvt","",speed); |
| if ((n = ttvt(speed,flow)) < 0) { /* Enter "virtual terminal" mode */ |
| if (!network) { |
| debug(F101,"CONNECT ttvt","",n); |
| tthang(); /* Hang up and close the device. */ |
| ttclos(0); |
| dologend(); |
| if (ttopen(ttname, /* Open it again... */ |
| &local, |
| network ? -nettype : mdmtyp, |
| 0 |
| ) < 0) { |
| ckmakmsg(temp,TMPLEN,"Sorry, can't reopen ",ttname,NULL,NULL); |
| perror(temp); |
| goto conret0; |
| } |
| #ifdef IKS_OPTION |
| if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start) |
| return(0); |
| #endif /* IKS_OPTION */ |
| if (ttvt(speed,flow) < 0) { /* Try virtual terminal mode again. */ |
| conres(); /* Failure this time is fatal. */ |
| printf("Sorry, Can't condition communication line\n"); |
| goto conret0; |
| } |
| } |
| } |
| debug(F101,"CONNECT ttvt ok, escape","",escape); |
| |
| debug(F101,"CONNECT carrier-watch","",carrier); |
| if ((!network |
| #ifdef TN_COMPORT |
| || istncomport() |
| #endif /* TN_COMPORT */ |
| ) && (carrier != CAR_OFF)) { |
| int x; |
| x = ttgmdm(); |
| debug(F100,"CONNECT ttgmdm","",x); |
| if ((x > -1) && !(x & BM_DCD)) { |
| #ifndef NOHINTS |
| extern int hints; |
| #endif /* NOHINTS */ |
| debug(F100,"CONNECT ttgmdm CD test fails","",x); |
| conres(); |
| printf("?Carrier required but not detected.\n"); |
| #ifndef NOHINTS |
| if (!hints) |
| return(0); |
| printf("***********************************\n"); |
| printf(" Hint: To CONNECT to a serial device that\n"); |
| printf(" is not presenting the Carrier Detect signal,\n"); |
| printf(" first tell C-Kermit to:\n\n"); |
| printf(" SET CARRIER-WATCH OFF\n\n"); |
| printf("***********************************\n\n"); |
| #endif /* NOHINTS */ |
| goto conret0; |
| } |
| debug(F100,"CONNECT ttgmdm ok","",0); |
| } |
| #ifndef NOCSETS |
| /* Set up character set translations */ |
| |
| unicode = 0; /* Assume Unicode won't be involved */ |
| tcs = 0; /* "Transfer" or "Other" charset */ |
| sxo = rxo = NULL; /* Initialize byte-to-byte functions */ |
| sxi = rxi = NULL; |
| if (tcsr != tcsl) { /* Remote and local sets differ... */ |
| #ifdef UNICODE |
| if (tcsr == FC_UTF8 || /* Remote charset is UTF-8 */ |
| tcsl == FC_UTF8) { /* or local one is. */ |
| xuf = xl_ufc[tcsl]; /* Incoming Unicode to local */ |
| if (xuf || tcsl == FC_UTF8) { |
| tcs = (tcsr == FC_UTF8) ? tcsl : tcsr; /* The "other" set */ |
| xfu = xl_fcu[tcs]; /* Local byte to remote Unicode */ |
| if (xfu) |
| unicode = (tcsr == FC_UTF8) ? 1 : 2; |
| } |
| tcssize = fcsinfo[tcs].size; /* Size of other character set. */ |
| } else { |
| #endif /* UNICODE */ |
| tcs = gettcs(tcsr,tcsl); /* Get intermediate set. */ |
| 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]; |
| #ifdef UNICODE |
| } |
| #endif /* UNICODE */ |
| } |
| /* |
| 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; |
| |
| #ifdef COMMENT |
| #ifdef DEBUG |
| if (deblog) { |
| debug(F101,"CONNECT tcs","",tcs); |
| debug(F101,"CONNECT tcsl","",tcsl); |
| debug(F101,"CONNECT tcsr","",tcsr); |
| debug(F101,"CONNECT fcsinfo[tcsl].size","",fcsinfo[tcsl].size); |
| debug(F101,"CONNECT fcsinfo[tcsr].size","",fcsinfo[tcsr].size); |
| debug(F101,"CONNECT unicode","",unicode); |
| } |
| #endif /* DEBUG */ |
| #endif /* COMMENT */ |
| |
| #ifdef CK_XYZ |
| #ifndef XYZ_INTERNAL |
| { |
| extern int binary; /* See about ZMODEM autodownloads */ |
| char * s; |
| s = binary ? ptab[PROTO_Z].p_b_rcmd : ptab[PROTO_Z].p_t_rcmd; |
| if (!s) s = ""; |
| zmdlok = (*s != NUL); /* OK if we have external commands */ |
| } |
| #endif /* XYZ_INTERNAL */ |
| #endif /* CK_XYZ */ |
| |
| #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_ON); |
| apcactive = 0; /* An APC command is not active */ |
| apclength = 0; /* ... */ |
| #endif /* CK_APC */ |
| inesc = ES_NORMAL; /* Initial state of recognizer */ |
| debug(F101,"CONNECT escseq","",escseq); |
| #endif /* NOESCSEQ */ |
| |
| parent_id = getpid(); /* Get parent's pid for signalling */ |
| debug(F101,"CONNECT parent pid","",parent_id); |
| |
| if (xpipe[0] > -1) /* If old pipe hanging around, close */ |
| close(xpipe[0]); |
| xpipe[0] = -1; |
| if (xpipe[1] > -1) |
| close(xpipe[1]); |
| xpipe[1] = -1; |
| goterr = 0; /* Error flag for pipe & fork */ |
| if (pipe(xpipe) != 0) { /* Create new pipe to pass info */ |
| perror("Can't make pipe"); /* between forks. */ |
| debug(F101,"CONNECT pipe error","",errno); |
| goterr = 1; |
| } else |
| #ifdef BEOSORBEBOX |
| { |
| pid = spawn_thread(concld, "Lower Fork", B_NORMAL_PRIORITY, NULL); |
| resume_thread(pid); |
| } |
| #else |
| if ((pid = fork()) == (PID_T) -1) { /* Pipe OK, make port fork. */ |
| perror("Can't make port fork"); |
| debug(F101,"CONNECT fork error","",errno); |
| goterr = 1; |
| } |
| #endif /* BEOSORBEBOX */ |
| debug(F101,"CONNECT created fork, pid","",pid); |
| if (goterr) { /* Failed to make pipe or fork */ |
| conres(); /* Reset the console. */ |
| if (msgflg) { |
| printf("\r\nCommunications disconnect (Back at %s)\r\n", |
| *myhost ? |
| myhost : |
| #ifdef UNIX |
| "local UNIX system" |
| #else |
| "local system" |
| #endif /* UNIX */ |
| ); |
| } |
| printf("\n"); |
| what = W_NOTHING; /* So console modes are set right. */ |
| #ifndef NOCSETS |
| language = langsv; /* Restore language */ |
| #endif /* NOCSETS */ |
| parent_id = (PID_T) 0; /* Clean up */ |
| goto conret1; |
| } |
| debug(F101,"CONNECT fork pid","",pid); |
| |
| /* Upper fork (KEYB fork) reads keystrokes and sends them out. */ |
| |
| if (pid) { /* pid != 0, so I am the upper fork. */ |
| /* |
| Before doing anything significant, the child fork must wait for a go-ahead |
| character from xpipe[0]. Before starting to wait, we have enough time to |
| clear buffers and set up the signal handler. When done with this, we will |
| allow the child to continue by satisfying its pending read. |
| |
| Remember the child and parent have separate address space. The child has |
| its own copy of input buffers, so we must clear the input buffers in the |
| parent. Otherwise strange effects may occur, like chunks of characters |
| repeatedly echoed on terminal screen. The child process is designed to |
| empty its input buffers by reading all available characters and either |
| echoing them on the terminal screen or saving them for future use in the |
| parent. The latter case happens during APC processing - see the code around |
| CEV_APC occurrences to see how the child passes its ibuf etc to parent via |
| xpipe, for preservation until the next entry to this module, to ensure that |
| no characters are lost between CONNECT sessions. |
| */ |
| |
| /* |
| This one needs a bit of extra explanation... In addition to the CONNECT |
| module's own buffers, which are communicated and synchronized via xpipe, |
| the low-level UNIX communication routines (ttinc, ttxin, etc) are also |
| buffered, statically, in the ckutio.c module. But when the two CONNECT |
| forks split off, the lower fork is updating this buffer's pointers and |
| counts, but the upper fork never finds out about it and still has the old |
| ones. The following UNIX-specific call to the ckutio.c module takes care |
| of this... Without it, we get dual echoing of incoming characters. |
| */ |
| ttflux(); |
| /* |
| At this point, perhaps you are wondering why we use forks at all. It is |
| simply because there is no other method portable among all UNIX variations. |
| Not threads, not select(), ... (Yes, select() is more common now; it might |
| actually be worth writing a variation of this module that works like BSD |
| Telnet, one fork, driven by select()). |
| */ |
| ibp = ibuf; /* Clear ibuf[]. */ |
| ibc = 0; /* Child now has its own copy */ |
| signal(CK_FORK_SIG, pipeint); /* Handler for messages from child. */ |
| write(xpipe[1], ibuf, 1); /* Allow child to proceed */ |
| close(xpipe[1]); xpipe[1] = -1; /* Parent - prevent future writes */ |
| |
| what = W_CONNECT; /* Keep track of what we're doing */ |
| active = 1; |
| debug(F101,"CONNECT keyboard fork duplex","",duplex); |
| /* |
| Catch communication errors or mode changes in lower fork. |
| |
| Note: Some C compilers (e.g. Cray UNICOS) interpret the ANSI C standard |
| about setjmp() in a way that disallows constructions like: |
| |
| if ((var = [sig]setjmp(env)) == 0) ... |
| |
| which prevents the value returned by cklongjmp() from being used at all. |
| So the signal handlers set a global variable, sjval, instead. |
| */ |
| if ( |
| #ifdef CK_POSIX_SIG |
| sigsetjmp(con_env,1) |
| #else |
| setjmp(con_env) |
| #endif /* CK_POSIX_SIG */ |
| == 0) { /* Normal entry... */ |
| jbset = 1; /* Now we have a longjmp buffer */ |
| sjval = CEV_NO; /* Initialize setjmp return code. */ |
| |
| debug(F101,"CONNECT setjmp normal entry","",sjval); |
| |
| #ifdef ANYX25 |
| if (network && (nettype == NET_SX25 || nettype == NET_IX25)) { |
| obufl = 0; |
| bzero (x25obuf,sizeof(x25obuf)); |
| } |
| #endif /* ANYX25 */ |
| /* |
| Here is the big loop that gets characters from the keyboard and sends them |
| out the communication device. There are two components to the communication |
| path: the connection from the keyboard to C-Kermit, and from C-Kermit to |
| the remote computer. 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 5A(164). |
| */ |
| while (active) { |
| #ifndef NOSETKEY |
| if (kmptr) { /* Have current macro? */ |
| debug(F100,"CONNECT kmptr non NULL","",0); |
| if ((c = (CHAR) *kmptr++) == NUL) { /* Get char from it */ |
| kmptr = NULL; /* If no more chars, */ |
| debug(F100,"CONNECT macro empty, continuing","",0); |
| continue; /* reset pointer and continue */ |
| } |
| debug(F000,"CONNECT char from macro","",c); |
| } else /* No macro... */ |
| #endif /* NOSETKEY */ |
| c = CONGKS(); /* Read from keyboard */ |
| |
| #ifdef COMMENT |
| /* too much... */ |
| debug(F101,"CONNECT ** KEYB","",c); |
| #endif /* COMMENT */ |
| if (c == -1) { /* If read() got an error... */ |
| debug(F101,"CONNECT keyboard read errno","",errno); |
| #ifdef COMMENT |
| /* |
| This seems to cause problems. If read() returns -1, the signal has already |
| been delivered, and nothing will wake up the pause(). |
| */ |
| pause(); /* Wait for transmitter to finish. */ |
| #else |
| #ifdef A986 |
| /* |
| On Altos machines with Xenix 3.0, pressing DEL in connect mode brings us |
| here (reason unknown). The console line discipline at this point has |
| intr = ^C. The communications tty has intr = DEL but we get here after |
| pressing DEL on the keyboard, even when the remote system has been set not |
| to echo. With A986 defined, we stay in the read loop and beep only if the |
| offending character is not DEL. |
| */ |
| if ((c & 127) != 127) conoc(BEL); |
| #else |
| #ifdef EINTR |
| /* |
| This can be caused by the other fork signalling this one about |
| an echoing change during TELNET negotiations. |
| */ |
| if (errno == EINTR) |
| continue; |
| #endif /* EINTR */ |
| conoc(BEL); /* Otherwise, beep */ |
| active = 0; /* and terminate the read loop */ |
| continue; |
| #endif /* A986 */ |
| #endif /* COMMENT */ |
| } |
| c &= cmdmsk; /* Do any requested masking */ |
| #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 */ |
| (tt_escape && (c & 0xff) == escape)) { /* Escape char? */ |
| debug(F000,"CONNECT got escape","",c); |
| c = CONGKS() & 0177; /* Got esc, get its arg */ |
| /* No key mapping here */ |
| doesc((char) c); /* Now process it */ |
| |
| } else { /* It's not the escape character */ |
| csave = c; /* Save it before translation */ |
| /* for local echoing. */ |
| #ifndef NOCSETS |
| if (inesc == ES_NORMAL) { /* If not inside escape seq.. */ |
| /* Translate character sets */ |
| #ifdef UNICODE |
| int x; |
| CHAR ch; |
| ch = c; |
| if (unicode == 1) { /* Remote is UTF-8 */ |
| outxcount = b_to_u(ch,outxbuf,OUTXBUFSIZ,tcssize); |
| outxbuf[outxcount] = NUL; |
| } else if (unicode == 2) { /* Local is UTF-8 */ |
| x = u_to_b(ch); /* So translate to remote byte */ |
| if (x < 0) |
| continue; |
| outxbuf[0] = (unsigned)(x & 0xff); |
| outxcount = 1; |
| outxbuf[outxcount] = NUL; |
| } else { |
| #endif /* UNICODE */ |
| /* Local-to-intermediate */ |
| if (sxo) c = (*sxo)((char)c); |
| /* Intermediate-to-remote */ |
| if (rxo) c = (*rxo)((char)c); |
| outxbuf[0] = c; |
| outxcount = 1; |
| outxbuf[outxcount] = NUL; |
| #ifdef UNICODE |
| } |
| #endif /* UNICODE */ |
| } |
| if (escseq) |
| apcrc = chkaes((char)c); |
| #else |
| outxbuf[0] = c; |
| outxcount = 1; |
| outxbuf[outxcount] = NUL; |
| #endif /* NOCSETS */ |
| for (i = 0; i < outxcount; i++) { |
| c = outxbuf[i]; |
| /* |
| If Shift-In/Shift-Out is 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, */ |
| ttoc(dopar(SO)); /* shift. */ |
| outshift = 1; |
| } |
| } else { |
| if (outshift == 1) { /* 7-bit character */ |
| ttoc(dopar(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. */ |
| #ifdef SUNX25 |
| if (network && nettype == NET_SX25) { |
| if (padparms[PAD_ECHO]) { |
| if (debses) |
| conol(dbchr(c)) ; |
| else |
| if ((c != padparms[PAD_CHAR_DELETE_CHAR]) && |
| (c != padparms[PAD_BUFFER_DELETE_CHAR]) && |
| (c != padparms[PAD_BUFFER_DISPLAY_CHAR])) |
| conoc(c) ; |
| if (seslog && !sessft) |
| logchar(c); |
| } |
| if (c == CR && (padparms[PAD_LF_AFTER_CR] == 4 || |
| padparms[PAD_LF_AFTER_CR] == 5)) { |
| if (debses) |
| conol(dbchr(LF)) ; |
| else |
| conoc(LF) ; |
| if (seslog && !sessft) |
| logchar(LF); |
| } |
| if (c == padparms[PAD_BREAK_CHARACTER]) { |
| breakact(); |
| } else if (padparms[PAD_DATA_FORWARD_TIMEOUT]) { |
| tosend = 1; |
| x25obuf [obufl++] = c; |
| } else if (((c == padparms[PAD_CHAR_DELETE_CHAR])|| |
| (c == padparms[PAD_BUFFER_DELETE_CHAR]) || |
| (c == padparms[PAD_BUFFER_DISPLAY_CHAR])) |
| && (padparms[PAD_EDITING])) { |
| if (c == padparms[PAD_CHAR_DELETE_CHAR]) { |
| if (obufl > 0) { |
| conol("\b \b"); obufl--; |
| } else {} |
| } else if |
| (c == padparms[PAD_BUFFER_DELETE_CHAR]) { |
| conol ("\r\nPAD Buffer Deleted\r\n"); |
| obufl = 0; |
| } else if |
| (c==padparms[PAD_BUFFER_DISPLAY_CHAR]) { |
| conol("\r\n"); |
| conol(x25obuf); |
| conol("\r\n"); |
| } else {} |
| } else { |
| x25obuf [obufl++] = c; |
| if (obufl == MAXOX25) tosend = 1; |
| else if (c == CR) tosend = 1; |
| } |
| if (tosend) { |
| if (ttol((CHAR *)x25obuf,obufl) < 0) { |
| perror ("\r\nCan't send characters"); |
| active = 0; |
| } else { |
| bzero (x25obuf,sizeof(x25obuf)); |
| obufl = 0; |
| tosend = 0; |
| } |
| } else {}; |
| } else { |
| #endif /* SUNX25 */ |
| if (c == '\015') { /* Carriage Return */ |
| int stuff = -1; |
| if (tnlm) { /* TERMINAL NEWLINE ON */ |
| stuff = LF; /* Stuff LF */ |
| #ifdef TNCODE |
| } else if (network && /* TELNET NEWLINE */ |
| IS_TELNET()) { |
| switch (!TELOPT_ME(TELOPT_BINARY) ? |
| tn_nlm : |
| tn_b_nlm |
| ) { |
| case TNL_CRLF: |
| stuff = LF; |
| break; |
| case TNL_CRNUL: |
| stuff = NUL; |
| break; |
| } |
| #endif /* TNCODE */ |
| } |
| if (stuff > -1) { |
| ttoc(dopar('\015')); /* Send CR */ |
| if (duplex) conoc('\015'); /* Echo CR */ |
| c = stuff; /* Char to stuff */ |
| csave = c; |
| } |
| } |
| #ifdef TNCODE |
| /* If user types the 0xff character (TELNET IAC), it must be doubled. */ |
| else /* Not CR */ |
| if ((dopar((CHAR) c) == IAC) && /* IAC (0xff) */ |
| network && IS_TELNET()) { |
| /* Send one copy now */ |
| /* and the other one just below. */ |
| ttoc((char)IAC); |
| } |
| #endif /* TNCODE */ |
| /* Send the character */ |
| |
| if (ttoc((char)dopar((CHAR) c)) > -1) { |
| if (duplex) { /* If half duplex, must echo */ |
| if (debses) |
| conol(dbchr(csave)); /* original char */ |
| else /* not the translated one */ |
| conoc((char)csave); |
| if (seslog) { /* And maybe log it too */ |
| c2 = csave; |
| if (sessft == 0 && csave == '\r') |
| c2 = '\n'; |
| logchar((char)c2); |
| } |
| } |
| } else { |
| perror("\r\nCan't send character"); |
| active = 0; |
| } |
| #ifdef SUNX25 |
| } |
| #endif /* SUNX25 */ |
| } /* for... */ |
| } |
| } |
| |
| /* now active == 0 */ |
| signal(CK_FORK_SIG, SIG_IGN); /* Turn off CK_FORK_SIG */ |
| sjval = CEV_NO; /* Set to hangup */ |
| } /* Come here on termination of child */ |
| |
| /* cklongjmp() executed in pipeint() (parent only!) comes here */ |
| |
| /* |
| Now the child fork is gone or is waiting for CK_FORK_SIG in ck_sndmsg(). |
| So we can't get (in the parent) any subsequent CK_FORK_SIG signals until |
| we signal the child with CK_FORK_SIG. |
| */ |
| debug(F100,"CONNECT signaling port fork","",0); |
| signal(CK_FORK_SIG, SIG_IGN); /* Turn this off */ |
| debug(F101,"CONNECT killing port fork","",pid); |
| if (pid) { |
| int x = 0; |
| #ifdef BEOSORBEBOX |
| { |
| long ret_val; |
| x = kill(pid,SIGKILLTHR); /* Kill lower fork */ |
| wait_for_thread(pid, &ret_val); |
| } |
| #else |
| #ifdef Plan9 |
| x = kill(pid,SIGKILL); /* Kill lower fork */ |
| #else |
| x = kill(pid,9); |
| #endif /* Plan9 */ |
| #endif /* BEOSORBEBOX */ |
| wait((WAIT_T *)0); /* Wait till gone. */ |
| if (x < 0) { |
| printf("WARNING: Failure to kill fork, pid %d: %s, errno=%d\n", |
| (int) pid, ck_errstr(), errno); |
| debug(F111,"CONNECT error killing pid",ck_errstr(),errno); |
| } |
| debug(F101,"CONNECT killed port fork","",pid); |
| pid = (PID_T) 0; |
| } |
| if (sjval == CEV_HUP) { /* Read error on comm device */ |
| dohangup = 1; /* so we want to hang up our side */ |
| #ifdef NETCONN |
| if (network) { /* and/or close network connection */ |
| ttclos(0); |
| dologend(); |
| #ifdef SUNX25 |
| if (nettype == NET_SX25) /* If X.25, restore the PAD params */ |
| initpad(); |
| #endif /* SUNX25 */ |
| } |
| #endif /* NETCONN */ |
| } |
| #ifdef CK_APC |
| if (sjval == CEV_APC) { /* Application Program Command rec'd */ |
| apcactive = APC_REMOTE; /* Flag APC as active */ |
| active = 0; /* Flag CONNECT as inactive */ |
| } |
| #endif /* CK_APC */ |
| 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. */ |
| } |
| |
| #ifdef COMMENT |
| #ifdef NETCONN |
| #ifdef SIGPIPE |
| if (network && sigpiph) /* Restore previous SIGPIPE handler */ |
| (VOID) signal(SIGPIPE, sigpiph); |
| #endif /* SIGPIPE */ |
| #endif /* NETCONN */ |
| #endif /* COMMENT */ |
| |
| #ifdef ANYX25 |
| if (dox25clr) { /* If X.25 Clear requested */ |
| x25clear(); /* do that. */ |
| #ifndef IBMX25 |
| initpad(); |
| #endif /* IBMX25 */ |
| dox25clr = 0; /* But only once. */ |
| } |
| #endif /* ANYX25 */ |
| |
| if (quitnow) doexit(GOOD_EXIT,xitsta); /* Exit now if requested. */ |
| if (msgflg) |
| printf("(Back at %s)", *myhost ? myhost : "local UNIX system"); |
| #ifdef CK_APC |
| if (!apcactive) |
| #endif /* CK_APC */ |
| printf("\n"); |
| what = W_NOTHING; /* So console modes set right. */ |
| #ifndef NOCSETS |
| language = langsv; /* Restore language */ |
| #endif /* NOCSETS */ |
| parent_id = (PID_T) 0; |
| goto conret1; |
| |
| } |
| #ifndef BEOSORBEBOX |
| else { /* *** */ /* Inferior reads, prints port input */ |
| concld(/* (void *)&pid */); |
| } |
| #endif /* BEOSORBEBOX */ |
| |
| conret1: |
| conret = 1; |
| conret0: |
| signal(CK_FORK_SIG, SIG_IGN); /* In case this wasn't done already */ |
| debug(F101,"CONNECT conect exit ibc","",ibc); |
| debug(F101,"CONNECT conect exit obc","",obc); |
| close(xpipe[0]); xpipe[0] = -1; /* Close the pipe */ |
| close(xpipe[1]); xpipe[1] = -1; |
| if (msgflg) { |
| #ifdef CK_APC |
| if (apcactive == APC_LOCAL) |
| printf("\n"); |
| #endif /* CK_APC */ |
| printf("----------------------------------------------------\r\n"); |
| } |
| fflush(stdout); |
| return(conret); |
| } |
| |
| |
| /* H C O N N E -- Give help message for connect. */ |
| |
| int |
| hconne() { |
| int c; |
| static char *hlpmsg[] = { |
| "\r\n ? for this message", |
| "\r\n 0 (zero) to send a null", |
| "\r\n B to send a BREAK", |
| #ifdef CK_LBRK |
| "\r\n L to send a Long BREAK", |
| #endif /* CK_LBRK */ |
| #ifdef NETCONN |
| "\r\n I to send a network interrupt packet", |
| #ifdef TCPSOCKET |
| "\r\n A to send Are You There?", |
| #endif /* TCPSOCKET */ |
| #ifdef ANYX25 |
| "\r\n R to reset X.25 virtual circuit", |
| #endif /* ANYX25 */ |
| #endif /* NETCONN */ |
| "\r\n U to hangup and close the connection", |
| "\r\n Q to hangup and quit Kermit", |
| "\r\n S for status", |
| #ifdef NOPUSH |
| "\r\n ! to push to local shell (disabled)", |
| "\r\n Z to suspend (disabled)", |
| #else |
| "\r\n ! to push to local shell", |
| #ifdef NOJC |
| "\r\n Z to suspend (disabled)", |
| #else |
| "\r\n Z to suspend", |
| #endif /* NOJC */ |
| #endif /* NOPUSH */ |
| "\r\n \\ backslash code:", |
| "\r\n \\nnn decimal character code", |
| "\r\n \\Onnn octal character code", |
| "\r\n \\Xhh hexadecimal character code", |
| "\r\n terminate with carriage return.", |
| "\r\n Type the escape character again to send the escape character, or", |
| "\r\n press the space-bar to resume the CONNECT command.\r\n", |
| "" }; |
| |
| conol("\r\n----------------------------------------------------"); |
| conol("\r\nPress C to return to "); |
| conol(*myhost ? myhost : "the C-Kermit prompt"); |
| conol(", or:"); |
| conola(hlpmsg); /* Print the help message. */ |
| conol("Command>"); /* Prompt for command. */ |
| c = CONGKS() & 0177; /* Get character, strip any parity. */ |
| /* No key mapping or translation here */ |
| if (c != CMDQ) |
| conoll(""); |
| 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 */ |
| /* doesc */ { |
| CHAR d; |
| |
| debug(F101,"CONNECT doesc","",c); |
| while (1) { |
| if (c == escape) { /* Send escape character */ |
| d = dopar((CHAR) c); ttoc((char) d); return; |
| } else /* Or else look it up below. */ |
| if (isupper(c)) c = tolower(c); |
| |
| switch(c) { |
| |
| case 'c': /* Escape back to prompt */ |
| case '\03': |
| active = 0; conol("\r\n"); return; |
| |
| case 'b': /* Send a BREAK signal */ |
| case '\02': |
| ttsndb(); return; |
| |
| #ifdef NETCONN |
| case 'i': /* Send Interrupt */ |
| case '\011': |
| #ifdef TCPSOCKET |
| #ifndef IP |
| #define IP 244 |
| #endif /* IP */ |
| if (network && IS_TELNET()) { /* TELNET */ |
| temp[0] = (CHAR) IAC; /* I Am a Command */ |
| temp[1] = (CHAR) IP; /* Interrupt Process */ |
| temp[2] = NUL; |
| ttol((CHAR *)temp,2); |
| } else |
| #endif /* TCPSOCKET */ |
| #ifdef SUNX25 |
| if (network && (nettype == NET_SX25)) { |
| (VOID) x25intr(0); /* X.25 interrupt packet */ |
| conol("\r\n"); |
| } else |
| #endif /* SUNX25 */ |
| conoc(BEL); |
| return; |
| |
| #ifdef TCPSOCKET |
| case 'a': /* "Are You There?" */ |
| case '\01': |
| #ifndef AYT |
| #define AYT 246 |
| #endif /* AYT */ |
| if (network && IS_TELNET()) { |
| temp[0] = (CHAR) IAC; /* I Am a Command */ |
| temp[1] = (CHAR) AYT; /* Are You There? */ |
| temp[2] = NUL; |
| ttol((CHAR *)temp,2); |
| } else conoc(BEL); |
| return; |
| #endif /* TCPSOCKET */ |
| #endif /* NETCONN */ |
| |
| #ifdef CK_LBRK |
| case 'l': /* Send a Long BREAK signal */ |
| ttsndlb(); return; |
| #endif /* CK_LBRK */ |
| |
| case 'u': /* Hangup */ |
| /* case '\010': */ /* No, too dangerous */ |
| #ifdef ANYX25 |
| if (network && (nettype == NET_SX25 || nettype == NET_IX25)) |
| dox25clr = 1; |
| else |
| #endif /* ANYX25 */ |
| dohangup = 2; active = 0; conol("\r\nHanging up "); return; |
| |
| #ifdef ANYX25 |
| case 'r': /* Reset the X.25 virtual circuit */ |
| case '\022': |
| if (network && (nettype == NET_SX25 || nettype == NET_IX25)) |
| (VOID) x25reset(0,0); |
| conol("\r\n"); |
| return; |
| #endif /* ANYX25 */ |
| |
| case 'q': /* Quit */ |
| dohangup = 2; quitnow = 1; active = 0; conol("\r\n"); return; |
| |
| case 's': /* Status */ |
| conoll(""); |
| conoll("----------------------------------------------------"); |
| #ifdef NETCMD |
| if (ttpipe) |
| ckmakmsg(temp,TMPLEN," Pipe: \"",ttname,"\"",NULL); |
| else |
| #endif /* NETCMD */ |
| ckmakmsg(temp, |
| TMPLEN, |
| " ", |
| (network ? "Host" : "Device"), |
| ": ", |
| ttname |
| ); |
| conoll(temp); |
| if (!network && speed >= 0L) { |
| sprintf(temp,"Speed %ld", speed); |
| conoll(temp); |
| } |
| sprintf(temp," Terminal echo: %s", duplex ? "local" : "remote"); |
| conoll(temp); |
| sprintf(temp," Terminal bytesize: %d", (cmask == 0177) ? 7 : 8); |
| conoll(temp); |
| sprintf(temp," Command bytesize: %d", (cmdmsk == 0177) ? 7 : 8 ); |
| conoll(temp); |
| if (hwparity) |
| sprintf(temp," Parity[hardware]: %s",parnam((char)hwparity)); |
| else |
| sprintf(temp," Parity: %s", parnam((char)parity)); |
| conoll(temp); |
| sprintf(temp," Autodownload: %s", autodl ? "on" : "off"); |
| conoll(temp); |
| ckmakmsg(temp, /* (would not be safe for sprintf) */ |
| TMPLEN, |
| " Session log: ", |
| *sesfil ? sesfil : "(none)", |
| NULL, |
| NULL |
| ); |
| conoll(temp); |
| #ifndef NOSHOW |
| if (!network) shomdm(); |
| #endif /* NOSHOW */ |
| #ifdef CKLOGDIAL |
| { |
| long z; |
| z = dologshow(0); |
| if (z > -1L) { |
| sprintf(temp," Elapsed time: %s",hhmmss(z)); |
| conoll(temp); |
| } |
| } |
| #endif /* CKLOGDIAL */ |
| conoll("----------------------------------------------------"); |
| return; |
| |
| case 'h': /* Help */ |
| case '?': /* Help */ |
| c = hconne(); continue; |
| |
| case '0': /* Send a null */ |
| c = '\0'; d = dopar((CHAR) c); ttoc((char) d); return; |
| |
| case 'z': case '\032': /* Suspend */ |
| #ifndef NOPUSH |
| if (!nopush) |
| stptrap(0); |
| else |
| conoc(BEL); |
| #else |
| conoc(BEL); |
| #endif /* NOPUSH */ |
| return; |
| |
| case '@': /* Start inferior command processor */ |
| case '!': |
| #ifndef NOPUSH |
| if (!nopush) { |
| conres(); /* Put console back to normal */ |
| zshcmd(""); /* Fork a shell. */ |
| if (conbin((char)escape) < 0) { |
| printf("Error resuming CONNECT session\n"); |
| active = 0; |
| } |
| } else conoc(BEL); |
| #else |
| conoc(BEL); |
| #endif /* NOPUSH */ |
| return; |
| |
| case SP: /* Space, ignore */ |
| return; |
| |
| default: /* Other */ |
| if (c == CMDQ) { /* Backslash escape */ |
| int x; |
| ecbp = ecbuf; |
| *ecbp++ = c; |
| while (((c = (CONGKS() & cmdmsk)) != '\r') && (c != '\n')) |
| *ecbp++ = c; |
| *ecbp = NUL; ecbp = ecbuf; |
| x = xxesc(&ecbp); /* Interpret it */ |
| if (x >= 0) { /* No key mapping here */ |
| c = dopar((CHAR) x); |
| ttoc((char) c); |
| return; |
| } else { /* Invalid backslash code. */ |
| conoc(BEL); |
| return; |
| } |
| } |
| conoc(BEL); return; /* Invalid esc arg, beep */ |
| } |
| } |
| } |
| #endif /* NOLOCAL */ |