|
|
1.1 root 1: char *connv = "CONNECT Command for UNIX, 5A(036) 8 Feb 92";
2:
3: /* C K U C O N -- Dumb terminal connection to remote system, for UNIX */
4: /*
5: Author: Frank da Cruz ([email protected], [email protected]),
6: Columbia University Center for Computing Activities.
7: First released January 1985.
8: Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
9: York. Permission is granted to any individual or institution to use, copy, or
10: redistribute this software so long as it is not sold for profit, provided this
11: copyright notice is retained.
12: */
13:
14: #include "ckcdeb.h" /* Common things first */
15:
16: #ifdef NEXT
17: #undef NSIG
18: #include <sys/wait.h> /* For wait() */
19: #endif /* NEXT */
20:
21: #include <signal.h> /* Signals */
22: #include <errno.h> /* Error numbers */
23:
24: #ifdef ZILOG /* Longjumps */
25: #include <setret.h>
26: #else
27: #include <setjmp.h>
28: #endif /* ZILOG */
29:
30: /* Kermit-specific includes */
31:
32: #include "ckcasc.h" /* ASCII characters */
33: #include "ckcker.h" /* Kermit things */
34: #include "ckucmd.h" /* For xxesc() prototype */
35: #include "ckcnet.h" /* Network symbols */
36: #ifndef NOCSETS
37: #include "ckcxla.h" /* Character set translation */
38: #endif /* NOCSETS */
39:
40: /* Internal function prototypes */
41:
42: _PROTOTYP( VOID doesc, (char) );
43: _PROTOTYP( VOID logchar, (char) );
44: _PROTOTYP( int hconne, (void) );
45:
46: #ifndef SIGUSR1 /* User-defined signals */
47: #define SIGUSR1 30
48: #endif /* SIGUSR1 */
49:
50: #ifndef SIGUSR2
51: #define SIGUSR2 31
52: #endif /* SIGUSR2 */
53:
54: /* External variables */
55:
56: extern int local, escape, duplex, parity, flow, seslog, sessft, debses,
57: mdmtyp, ttnproto, cmask, cmdmsk, network, nettype, deblog, sosi, tnlm,
58: xitsta, what, ttyfd, quiet, backgrd;
59: extern long speed;
60: extern char ttname[], sesfil[], myhost[];
61: #ifdef NETCONN
62: extern int tn_init;
63: #endif /* NETCONN */
64:
65: #ifndef NOSETKEY /* Keyboard mapping */
66: extern KEY *keymap; /* Single-character key map */
67: extern MACRO *macrotab; /* Key macro pointer table */
68: static MACRO kmptr = NULL; /* Pointer to current key macro */
69: #endif /* NOSETKEY */
70:
71: /* Global variables local to this module */
72:
73: static int quitnow = 0, /* <esc-char>Q was typed */
74: dohangup = 0, /* <esc-char>H was typed */
75: sjval = 0, /* Setjump return value */
76: goterr = 0, /* I/O error flag */
77: active = 0, /* Lower fork active flag */
78: shift = 0; /* SO/SI shift state */
79:
80: static char kbuf[10], *kbp; /* Keyboard buffer & pointer */
81: static PID_T parent_id = (PID_T)0; /* Process id of keyboard fork */
82: static char *lbp; /* Line buffer pointer */
83: static int lbc = 0; /* Line buffer count */
84: #define LBUFL 200 /* Line buffer length */
85: #define TMPLEN 50 /* Temp buffer length */
86: #ifdef DYNAMIC
87: static char *lbuf, *temp; /* Line and temp buffers */
88: #else
89: static char lbuf[LBUFL], temp[TMPLEN];
90: #endif /* DYNAMIC */
91:
92: /* SunLink X.25 items */
93:
94: #ifdef SUNX25
95: static char *p; /* General purpose pointer */
96: char x25ibuf[MAXIX25]; /* Input buffer */
97: char x25obuf[MAXOX25]; /* Output buffer */
98: int ibufl; /* Length of input buffer */
99: int obufl; /* Length of output buffer */
100: unsigned char tosend = 0;
101: int linkid, lcn;
102: static int dox25clr = 0;
103: CHAR padparms[MAXPADPARMS+1];
104: static int padpipe[2]; /* Pipe descriptor to pass PAD parms */
105: #endif /* SUNX25 */
106:
107: /* Character-set items */
108:
109: #ifndef NOCSETS
110: #ifdef CK_ANSIC /* ANSI C prototypes... */
111: extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */
112: extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */
113: static CHAR (*sxo)(CHAR); /* Local translation functions */
114: static CHAR (*rxo)(CHAR); /* for output (sending) terminal chars */
115: static CHAR (*sxi)(CHAR); /* and for input (receiving) terminal chars. */
116: static CHAR (*rxi)(CHAR);
117: #else /* Not ANSI C... */
118: extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); /* Character set */
119: extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); /* translation functions. */
120: static CHAR (*sxo)(); /* Local translation functions */
121: static CHAR (*rxo)(); /* for output (sending) terminal chars */
122: static CHAR (*sxi)(); /* and for input (receiving) terminal chars. */
123: static CHAR (*rxi)();
124: #endif /* CK_ANSIC */
125: extern int language; /* Current language. */
126: static int langsv; /* For remembering language setting. */
127: extern struct csinfo fcsinfo[]; /* File character set info. */
128: extern int tcsr, tcsl; /* Terminal character sets, remote & local. */
129: static int tcs; /* Intermediate ("transfer") character set. */
130:
131: #ifndef NOESCSEQ
132: /*
133: As of edit 178, the CONNECT command will skip past ANSI escape sequences
134: to avoid translating the characters within them. This allows the CONNECT
135: command to work correctly when connected to a remote host that uses a
136: 7-bit ISO 646 national character set, in which characters like '[' would
137: normally be translated into accented characters, ruining the terminal's
138: interpretation (and generation) of escape sequences.
139:
140: Escape sequences of non-ANSI/ISO-compliant terminals are not handled.
141: */
142: #ifndef SKIPESC
143: #define SKIPESC
144: #endif /* SKIPESC */
145: /*
146: States for the escape-sequence recognizer.
147: */
148: #define ES_NORMAL 0 /* Normal, not in escape sequence */
149: #define ES_GOTESC 1 /* Current character is ESC */
150: #define ES_ESCSEQ 2 /* Inside an escape sequence */
151: #define ES_GOTCSI 3 /* Inside a control sequence */
152: #define ES_STRING 4 /* Inside DCS,OSC,PM, or APC string */
153: #define ES_TERMIN 5 /* 1st char of string terminator */
154:
155: static int
156: skipesc = 0, /* Skip over ANSI escape sequences */
157: inesc = ES_NORMAL; /* State of sequence recognizer */
158: /*
159: ANSI escape sequence handling. Only the 7-bit form is treated, because
160: translation is not a problem in the 8-bit environment, in which all GL
161: characters are ASCII and no translation takes place. So we don't check
162: for the 8-bit single-character versions of CSI, DCS, OSC, APC, or ST.
163: Here is the ANSI sequence recognizer state table, followed by the code
164: that implements it.
165:
166: Definitions:
167: CAN = Cancel 01/08 Ctrl-X
168: SUB = Substitute 01/10 Ctrl-Z
169: DCS = Device Control Sequence 01/11 05/00 ESC P
170: CSI = Control Sequence Introducer 01/11 05/11 ESC [
171: ST = String Terminator 01/11 05/12 ESC \
172: OSC = Operating System Command 01/11 05/13 ESC ]
173: PM = Privacy Message 01/11 05/14 ESC ^
174: APC = Application Program Command 01/11 05/15 ESC _
175:
176: ANSI escape sequence recognizer:
177:
178: State Input New State ; Commentary
179:
180: NORMAL (start) ; Start in NORMAL state
181:
182: (any) CAN NORMAL ; ^X cancels
183: (any) SUB NORMAL ; ^Z cancels
184:
185: NORMAL ESC GOTESC ; Begin escape sequence
186: NORMAL other ; NORMAL control or graphic character
187:
188: GOTESC ESC ; Start again
189: GOTESC [ GOTCSI ; CSI
190: GOTESC P STRING ; DCS introducer, consume through ST
191: GOTESC ] STRING ; OSC introducer, consume through ST
192: GOTESC ^ STRING ; PM introducer, consume through ST
193: GOTESC _ STRING ; APC introducer, consume through ST
194: GOTESC 0..~ NORMAL ; 03/00 through 17/14 = Final character
195: GOTESC other ESCSEQ ; Intermediate or ignored control character
196:
197: ESCSEQ ESC GOTESC ; Start again
198: ESCSEQ 0..~ NORMAL ; 03/00 through 17/14 = Final character
199: ESCSEQ other ; Intermediate or ignored control character
200:
201: GOTCSI ESC GOTESC ; Start again
202: GOTCSI @..~ NORMAL ; 04/00 through 17/14 = Final character
203: GOTCSI other ; Intermediate char or ignored control char
204:
205: STRING ESC TERMIN ; Maybe have ST
206: STRING other ; Consume all else
207:
208: TERMIN \ NORMAL ; End of string
209: TERMIN other STRING ; Still in string
210: */
211: /*
212: chkaes() -- Check ANSI Escape Sequence.
213: Call with EACH character in input stream.
214: Sets global inesc variable according to escape sequence state.
215: */
216: VOID
217: #ifdef CK_ANSIC
218: chkaes(char c)
219: #else
220: chkaes(c) char c;
221: #endif /* CK_ANSIC */
222: /* chkaes */ {
223:
224: if (c == CAN || c == SUB) /* CAN and SUB cancel any sequence */
225: inesc = ES_NORMAL;
226: else /* Otherwise */
227: switch (inesc) { /* enter state switcher */
228:
229: case ES_NORMAL: /* NORMAL state */
230: if (c == ESC) /* Got an ESC */
231: inesc = ES_GOTESC; /* Change state to GOTESC */
232: break; /* Otherwise stay in NORMAL state */
233:
234: case ES_GOTESC: /* GOTESC state */
235: if (c == '[') /* Left bracket after ESC is CSI */
236: inesc = ES_GOTCSI; /* Change to GOTCSI state */
237: else if (c > 057 && c < 0177) /* Final character '0' thru '~' */
238: inesc = ES_NORMAL; /* Back to normal */
239: else if (c == 'P' || (c > 0134 && c < 0140)) /* P, [, ^, or _ */
240: inesc = ES_STRING; /* Switch to STRING-absorption state */
241: else if (c != ESC) /* ESC in an escape sequence... */
242: inesc = ES_ESCSEQ; /* starts a new escape sequence */
243: break; /* Intermediate or ignored ctrl char */
244:
245: case ES_ESCSEQ: /* ESCSEQ -- in an escape sequence */
246: if (c > 057 && c < 0177) /* Final character '0' thru '~' */
247: inesc = ES_NORMAL; /* Return to NORMAL state. */
248: else if (c == ESC) /* ESC ... */
249: inesc = ES_GOTESC; /* starts a new escape sequence */
250: break; /* Intermediate or ignored ctrl char */
251:
252: case ES_GOTCSI: /* GOTCSI -- In a control sequence */
253: if (c > 077 && c < 0177) /* Final character '@' thru '~' */
254: inesc = ES_NORMAL; /* Return to NORMAL. */
255: else if (c == ESC) /* ESC ... */
256: inesc = ES_GOTESC; /* starts over. */
257: break; /* Intermediate or ignored ctrl char */
258:
259: case ES_STRING: /* Inside a string */
260: if (c == ESC) /* ESC may be 1st char of terminator */
261: inesc = ES_TERMIN; /* Go see. */
262: break; /* Absorb all other characters. */
263:
264: case ES_TERMIN: /* May have a string terminator */
265: if (c == '\\') /* which must be backslash */
266: inesc = ES_NORMAL; /* If so, back to NORMAL */
267: else /* Otherwise */
268: inesc = ES_STRING; /* Back to string absorption. */
269: }
270: }
271: #endif /* NOESCSEQ */
272: #endif /* NOCSETS */
273:
274: /* Connect state parent/child communication signal handlers */
275:
276: static jmp_buf con_env; /* Environment pointer for connect errors */
277: /*
278: Note: Some C compilers (e.g. Cray UNICOS) interpret the ANSI C standard
279: about setjmp() in a way that disallows constructions like:
280:
281: if ((var = setjmp(env)) == 0) ...
282:
283: which prevents the value returned by longjmp() from being used at all.
284: So the following handlers set a global variable instead.
285: */
286: static
287: SIGTYP
288: conn_int(foo) int foo; { /* Modem read failure handler, */
289: sjval = 1; /* Set global variable */
290: signal(SIGUSR1,SIG_IGN); /* Disarm the interrupt */
291: longjmp(con_env,1); /* Notifies parent process to stop */
292: }
293:
294: static
295: SIGTYP
296: mode_chg(foo) int foo; {
297: /*
298: I think we could just put "pad(padpipe);" after the read() call below,
299: and skip the longjmp and fork-restarting business at "newfork:", but I
300: have no way to test this.
301: */
302: #ifdef SUNX25 /* X.25 read new params from pipe */
303: if (nettype == NET_SX25) {
304: read(padpipe[0],padparms,MAXPADPARMS);
305: debug(F100,"pad_chg","",0);
306: } else {
307: #endif /* SUNX25 */
308: duplex = 1 - duplex; /* Toggle duplex mode. */
309: signal(SIGUSR2,mode_chg); /* Re-arm the signal. */
310: debug(F101,"mode_chg duplex","",duplex);
311: #ifdef SUNX25
312: }
313: sjval = 2; /* Set global variable. */
314: longjmp(con_env,2);
315: #endif /* SUNX25 */
316: }
317:
318: /* C O N E C T -- Perform terminal connection */
319:
320: int
321: conect() {
322: PID_T pid; /* Process id of child (modem reader) */
323: int n; /* General purpose counter */
324:
325: int c; /* c is a character, but must be signed
326: integer to pass thru -1, which is the
327: modem disconnection signal, and is
328: different from the character 0377 */
329: int c2; /* A copy of c */
330: int csave; /* Another copy of c */
331: int tx; /* tn_doop() return code */
332: #ifdef SUNX25
333: int i; /* Worker for X.25 code*/
334: #endif /* SUNX25 */
335:
336: #ifdef DYNAMIC
337: if (!(lbuf = malloc(LBUFL+1))) { /* Allocate input line buffer */
338: printf("Sorry, CONNECT input buffer can't be allocated\n");
339: return(0);
340: }
341: if (!(temp = malloc(TMPLEN+1))) { /* Allocate temporary buffer */
342: printf("Sorry, CONNECT temporary buffer can't be allocated\n");
343: free(lbuf);
344: return(0);
345: }
346: #endif /* DYNAMIC */
347:
348: if (!local) {
349: #ifdef NETCONN
350: printf("Sorry, you must SET LINE or SET HOST first\n");
351: #else
352: printf("Sorry, you must SET LINE first\n");
353: #endif /* NETCONN */
354: return(0);
355: }
356: if (backgrd) {
357: printf(
358: "\r\nSorry, Kermit's CONNECT command can be used only in the foreground\r\n");
359: return(0);
360: }
361: if (speed < 0L && network == 0) {
362: printf("Sorry, you must SET SPEED first\n");
363: return(0);
364: }
365: #ifdef TCPSOCKET
366: if (network && (nettype != NET_TCPB)
367: #ifdef SUNX25
368: && (nettype != NET_SX25)
369: #endif /* SUNX25 */
370: ) {
371: printf("Sorry, network type not supported\n");
372: return(0);
373: }
374: #endif /* TCPSOCKET */
375:
376: if (ttyfd < 0) { /* If communication device not open */
377: debug(F111,"ckucon opening",ttname,0); /* Open it now */
378: if (ttopen(ttname,&local,mdmtyp,0) < 0) {
379: sprintf(temp,"Sorry, can't open %s",ttname);
380: perror(temp);
381: debug(F110,"ckucon open failure",temp,0);
382: return(0);
383: }
384: }
385: dohangup = 0; /* Hangup not requested yet */
386: #ifdef SUNX25
387: dox25clr = 0; /* X.25 clear not requested yet */
388: #endif /* SUNX25 */
389:
390: if (!quiet) {
391: #ifdef NETCONN
392: if (network) {
393: printf("Connecting to host %s",ttname);
394: #ifdef SUNX25
395: if (nettype == NET_SX25)
396: printf(", Link ID %d, LCN %d",linkid,lcn);
397: #endif /* SUNX25 */
398: } else {
399: #endif /* NETCONN */
400: printf("Connecting to %s",ttname);
401: if (speed > -1L) printf(", speed %ld",speed);
402: #ifdef NETCONN
403: }
404: #endif /* NETCONN */
405: printf(".\r\nThe escape character is %s (ASCII %d).\r\n",
406: dbchr(escape),escape);
407: printf("Type the escape character followed by C to get back,\r\n");
408: printf("or followed by ? to see other options.\r\n");
409: if (seslog) {
410: printf("(Session logged to %s, ",sesfil);
411: printf("%s)\r\n", sessft ? "binary" : "text");
412: }
413: if (debses) printf("Debugging Display...)\r\n");
414: }
415:
416: /* Condition console terminal and communication line */
417:
418: if (conbin(escape) < 0) {
419: printf("Sorry, can't condition console terminal\n");
420: return(0);
421: }
422: debug(F101,"connect cmask","",cmask);
423: debug(F101,"connect cmdmsk","",cmdmsk);
424: goterr = 0;
425: if ((n = ttvt(speed,flow)) < 0) { /* Enter "virtual terminal" mode */
426: debug(F101,"CONNECT ttvt","",n);
427: goterr = 1; /* Error recovery... */
428: tthang(); /* Hang up and close the device. */
429: ttclos(0);
430: if (ttopen(ttname,&local,mdmtyp,0) < 0) { /* Open it again... */
431: sprintf(temp,"Sorry, can't reopen %s",ttname);
432: perror(temp);
433: return(0);
434: }
435: if (ttvt(speed,flow) < 0) { /* Try virtual terminal mode again. */
436: conres(); /* Failure this time is fatal. */
437: printf("Sorry, Can't condition communication line\n");
438: return(0);
439: }
440: #ifdef NETCONN
441: if (network && ttnproto == NP_TELNET)
442: tn_ini(); /* Just in case ttopen didn't... */
443: #endif /* NETCONN */
444: }
445: debug(F101,"connect ttvt ok, escape","",escape);
446:
447: #ifndef NOCSETS
448: /* Set up character set translations */
449:
450: #ifdef KANJI
451: /* Kanji not supported yet */
452: if (fcsinfo[tcsr].alphabet == AL_JAPAN ||
453: fcsinfo[tcsl].alphabet == AL_JAPAN ) {
454: tcs = TC_TRANSP;
455: } else
456: #endif /* KANJI */
457: #ifdef CYRILLIC
458: if (fcsinfo[tcsl].alphabet == AL_CYRIL) {
459: tcs = TC_CYRILL;
460: } else
461: #endif /* CYRILLIC */
462: tcs = TC_1LATIN;
463:
464: if (tcsr == tcsl) { /* Remote and local sets the same? */
465: sxo = rxo = NULL; /* If so, no translation. */
466: sxi = rxi = NULL;
467: } else { /* Otherwise, set up */
468: sxo = xls[tcs][tcsl]; /* translation function */
469: rxo = xlr[tcs][tcsr]; /* pointers for output functions */
470: sxi = xls[tcs][tcsr]; /* and for input functions. */
471: rxi = xlr[tcs][tcsl];
472: }
473: /*
474: This is to prevent use of zmstuff() and zdstuff() by translation functions.
475: They only work with disk i/o, not with communication i/o. Luckily Russian
476: translation functions don't do any stuffing...
477: */
478: langsv = language;
479: #ifndef NOCYRIL
480: if (language != L_RUSSIAN)
481: #endif /* NOCYRIL */
482: language = L_USASCII;
483:
484: #ifdef SKIPESC
485: /*
486: We need to activate the "skip escape sequence" feature when:
487: (a) translation is elected, and
488: (b) the local and/or remote set is 7-bit set other than US ASCII.
489: */
490: skipesc = (tcs != TC_TRANSP) && /* Not transparent */
491: (fcsinfo[tcsl].size == 128 || fcsinfo[tcsr].size == 128) && /* 7 bits */
492: (fcsinfo[tcsl].code != FC_USASCII);
493: inesc = ES_NORMAL; /* Initial state of recognizer */
494: #ifdef COMMENT
495: debug(F101,"tcs","",tcs);
496: debug(F101,"tcsl","",tcsl);
497: debug(F101,"tcsr","",tcsr);
498: debug(F101,"fcsinfo[tcsl].size","",fcsinfo[tcsl].size);
499: debug(F101,"fcsinfo[tcsr].size","",fcsinfo[tcsr].size);
500: #endif /* COMMENT */
501: debug(F101,"skipesc","",skipesc);
502: #endif /* SKIPESC */
503: #endif /* NOCSETS */
504:
505: /*
506: This is a label we jump back to when the lower fork sensed the need
507: to change modes. As of 5A(178), this is used only by X.25 code
508: (perhaps unnecessarily? -- The X.25 code needs a lot of testing and
509: cleaning up...)
510: */
511: newfork:
512: debug(F100,"CONNECT newfork","",0);
513: parent_id = getpid(); /* Get parent id for signalling */
514: signal(SIGUSR1,SIG_IGN); /* Don't kill myself */
515: #ifdef SUNX25
516: pipe(padpipe); /* Create pipe to pass PAD parms */
517: #endif /* SUNX25 */
518: #ifdef OXOS
519: /*
520: Because of the "extended security controls" in Olivetti X/OS,
521: the killing and killed process must have the same REAL uid.
522: Otherwise kill() gets ESRCH.
523: */
524: priv_on();
525: #endif /* OXOS */
526: pid = fork(); /* All ok, make a fork */
527: if (pid == -1) { /* Can't create it. */
528: conres(); /* Reset the console. */
529: perror("Can't create keyboard fork");
530: if (!quiet) {
531: printf("\r\nCommunications disconnect (Back at %s)\r\n",
532: *myhost ?
533: myhost :
534: #ifdef COHERENT
535: "Local COHERENT system"
536:
537: #else
538: #ifdef UNIX
539: "local UNIX system"
540: #else
541: "local system"
542: #endif /* UNIX */
543:
544:
545: #endif /* COHERENT */
546:
547: );
548: }
549: printf("\n");
550: what = W_NOTHING; /* So console modes are set right. */
551: #ifndef NOCSETS
552: language = langsv; /* Restore language */
553: #endif /* NOCSETS */
554: parent_id = (PID_T) 0; /* Clean up */
555: #ifdef DYNAMIC
556: if (temp) free(temp);
557: if (lbuf) free(lbuf); /* Free allocated memory */
558: #endif /* DYNAMIC */
559: return(1);
560: }
561: if (pid) { /* This fork reads, sends keystrokes */
562: what = W_CONNECT; /* Keep track of what we're doing */
563: active = 1;
564: debug(F101,"CONNECT keyboard fork duplex","",duplex);
565:
566: /* Catch communication errors or mode changes in lower fork */
567:
568: if (setjmp(con_env) == 0) { /* Normal entry... */
569: sjval = 0; /* Initialize setjmp return code. */
570: signal(SIGUSR1,conn_int); /* Routine for child process exit. */
571: signal(SIGUSR2,mode_chg); /* Routine for mode change. */
572: #ifdef SUNX25
573: if (network && nettype == NET_SX25) {
574: obufl = 0;
575: bzero (x25obuf,sizeof(x25obuf)) ;
576: }
577: #endif /* SUNX25 */
578:
579: /*
580: Here is the big loop that gets characters from the keyboard and sends them
581: out the communication device. There are two components to the communication
582: path: the connection from the keyboard to C-Kermit, and from C-Kermit to
583: the remote computer. The treatment of the 8th bit of keyboard characters
584: is governed by SET COMMAND BYTESIZE (cmdmsk). The treatment of the 8th bit
585: of characters sent to the remote is governed by SET TERMINAL BYTESIZE
586: (cmask). This distinction was introduced in edit 5A(164).
587: */
588: while (active) {
589: #ifndef NOSETKEY
590: if (kmptr) { /* Have current macro? */
591: if ((c = (CHAR) *kmptr++) == NUL) { /* Get char from it */
592: kmptr = NULL; /* If no more chars, */
593: continue; /* reset pointer and continue */
594: }
595: } else /* No macro... */
596: #endif /* NOSETKEY */
597: c = congks(0); /* Read from keyboard */
598:
599: debug(F111,"** KEYB",dbchr(c),c);
600:
601: if (c == -1) { /* If read() got an error... */
602: #ifdef COMMENT
603: /*
604: This seems to cause problems. If read() returns -1, the signal has already
605: been delivered, and nothing will wake up the pause().
606: */
607: pause(); /* Wait for transmitter to finish. */
608: #else
609: #ifdef A986
610: /*
611: On Altos machines with Xenix 3.0, pressing DEL in connect mode brings us
612: here (reason unknown). The console line discipline at this point has
613: intr = ^C. The communications tty has intr = DEL but we get here after
614: pressing DEL on the keyboard, even when the remote system has been set not
615: to echo. With A986 defined, we stay in the read loop and beep only if the
616: offending character is not DEL.
617: */
618: if ((c & 127) != 127) conoc(BEL);
619: #else
620: conoc(BEL); /* Beep */
621: active = 0; /* and terminate the read loop */
622: continue;
623: #endif /* A986 */
624: #endif /* COMMENT */
625: }
626: c &= cmdmsk; /* Do any requested masking */
627: #ifndef NOSETKEY
628: /*
629: Note: kmptr is NULL if we got character c from the keyboard, and it is
630: not NULL if it came from a macro. In the latter case, we must avoid
631: expanding it again.
632: */
633: if (!kmptr && macrotab[c]) { /* Macro definition for c? */
634: kmptr = macrotab[c]; /* Yes, set up macro pointer */
635: continue; /* and restart the loop, */
636: } else c = keymap[c]; /* else use single-char keymap */
637: #endif /* NOSETKEY */
638: if (
639:
640: #ifndef NOSETKEY
641: !kmptr &&
642: #endif /* NOSETKEY */
643: ((c & 0x7f) == escape)) { /* Escape character? */
644: debug(F000,"connect got escape","",c);
645: c = congks(0) & 0177; /* Got esc, get its arg */
646: /* No key mapping here */
647: doesc(c); /* Now process it */
648:
649: } else { /* It's not the escape character */
650: csave = c; /* Save it before translation */
651: /* for local echoing. */
652: #ifndef NOCSETS
653: #ifndef SKIPESC
654: /* Translate character sets */
655: if (sxo) c = (*sxo)(c); /* From local to intermediate. */
656: if (rxo) c = (*rxo)(c); /* From intermediate to remote. */
657: #else
658: if (inesc == ES_NORMAL) { /* If not inside escape seq.. */
659: /* Translate character sets */
660: if (sxo) c = (*sxo)(c); /* Local to intermediate. */
661: if (rxo) c = (*rxo)(c); /* Intermediate to remote. */
662: }
663: if (skipesc) chkaes(c); /* Check escape sequence status */
664: #endif /* SKIPESC */
665: #endif /* NOCSETS */
666: /*
667: If Shift-In/Shift-Out is selected and we have a 7-bit connection,
668: handle shifting here.
669: */
670: if (sosi) { /* Shift-In/Out selected? */
671: if (cmask == 0177) { /* In 7-bit environment? */
672: if (c & 0200) { /* 8-bit character? */
673: if (shift == 0) { /* If not shifted, */
674: ttoc(dopar(SO)); /* shift. */
675: shift = 1;
676: }
677: } else {
678: if (shift == 1) { /* 7-bit character */
679: ttoc(dopar(SI)); /* If shifted, */
680: shift = 0; /* unshift. */
681: }
682: }
683: }
684: if (c == SO) shift = 1; /* User typed SO */
685: if (c == SI) shift = 0; /* User typed SI */
686: }
687: c &= cmask; /* Apply Kermit-to-host mask now. */
688: #ifdef NETCONN
689: #ifdef SUNX25
690: if (network && nettype == NET_SX25) {
691: if (padparms[PAD_ECHO]) {
692: if (debses)
693: conol(dbchr(c)) ;
694: else
695: if ((c != padparms[PAD_CHAR_DELETE_CHAR]) &&
696: (c != padparms[PAD_BUFFER_DELETE_CHAR]) &&
697: (c != padparms[PAD_BUFFER_DISPLAY_CHAR]))
698: conoc(c) ;
699: if (seslog) logchar(c);
700: }
701: if (c == padparms[PAD_BREAK_CHARACTER])
702: breakact();
703: else if (padparms[PAD_DATA_FORWARD_TIMEOUT]) {
704: tosend = 1;
705: x25obuf [obufl++] = c;
706: } else if (((c == padparms[PAD_CHAR_DELETE_CHAR]) ||
707: (c == padparms[PAD_BUFFER_DELETE_CHAR]) ||
708: (c == padparms[PAD_BUFFER_DISPLAY_CHAR]))
709: && (padparms[PAD_EDITING]))
710: if (c == padparms[PAD_CHAR_DELETE_CHAR])
711: if (obufl > 0) {
712: conol("\b \b"); obufl--;
713: } else {}
714: else if (c == padparms[PAD_BUFFER_DELETE_CHAR]) {
715: conol ("\r\nPAD Buffer Deleted\r\n");
716: obufl = 0;
717: }
718: else if (c == padparms[PAD_BUFFER_DISPLAY_CHAR]) {
719: conol("\r\n");
720: conol(x25obuf);
721: conol("\r\n");
722: } else {}
723: else {
724: x25obuf [obufl++] = c;
725: if (obufl == MAXOX25) tosend = 1;
726: else if (c == CR) tosend = 1;
727: }
728: if (tosend)
729: if (ttol(x25obuf,obufl) < 0) {
730: perror ("\r\nCan't send characters");
731: active = 0;
732: } else {
733: bzero (x25obuf,sizeof(x25obuf));
734: obufl = 0;
735: tosend = 0;
736: } else {};
737: } else {
738: #endif /* SUNX25 */
739:
740: /*
741: This is for telnetting to IBM mainframes in linemode.
742: Blank lines (when you hit two CRs in a row) are normally ignored.
743: According to the telnet RFC, we must send both CR and LF.
744: Also, if the user types the 0xff character, which happens to be the
745: telnet IAC character, then it must be doubled.
746: */
747: if (network && ttnproto == NP_TELNET) {
748: if (c == '\015' && duplex != 0) {
749: ttoc(dopar('\015'));
750: conoc('\015');
751: csave = c = '\012';
752: } else if (c == IAC && parity == 0)
753: ttoc(IAC);
754: } else
755: #endif /* NETCONN */
756: if (c == '\015' && tnlm) { /* TERMINAL NEWLINE ON ? */
757: ttoc(dopar('\015')); /* Send CR as CRLF */
758: if (debses) conol(dbchr('\015'));
759: csave = c = '\012';
760: }
761:
762: /* Send the character */
763:
764: if (ttoc(dopar(c)) > -1) {
765: if (duplex) { /* If half duplex, must echo */
766: if (debses)
767: conol(dbchr(csave)); /* the original char */
768: else /* not the translated one */
769: conoc(csave);
770: if (seslog) { /* And maybe log it too */
771: c2 = csave;
772: if (sessft == 0 && csave == '\r')
773: c2 = '\n';
774: logchar(c2);
775: }
776: }
777: } else {
778: perror("\r\nCan't send character");
779: active = 0;
780: }
781: #ifdef SUNX25
782: }
783: #endif /* SUNX25 */
784: }
785: }
786: } /* Come here on death of child */
787: debug(F100,"CONNECT killing port fork","",0);
788: kill((int)pid,9); /* Done, kill inferior fork. */
789: #ifdef OXOS
790: /* See comments above about Olivetti X/OS. */
791: priv_off();
792: #endif /* OXOS */
793: wait((WAIT_T *)0); /* Wait till gone. */
794: if (sjval == 1) { /* Read error on comm device */
795: dohangup = 1;
796: #ifdef NETCONN
797: if (network) {
798: ttclos(0);
799: #ifdef SUNX25
800: if (nettype == NET_SX25) initpad();
801: #endif /* SUNX25 */
802: }
803: #endif /* NETCONN */
804: }
805: if (sjval == 2) /* If it was a mode change, go back */
806: goto newfork; /* and coordinate with other fork. */
807: conres(); /* Reset the console. */
808: if (quitnow) doexit(GOOD_EXIT,xitsta); /* Exit now if requested. */
809: if (dohangup) { /* If hangup requested, do that. */
810: tthang(); /* And make sure we don't hang up */
811: dohangup = 0; /* again unless requested again. */
812: }
813: #ifdef SUNX25
814: if (dox25clr) { /* If X.25 clear requested */
815: x25clear(); /* do that. */
816: initpad();
817: dox25clr = 0; /* But only once. */
818: }
819: #endif /* SUNX25 */
820: if (!quiet)
821: #ifdef COHERENT
822: printf("(Back at %s)", *myhost ? myhost : "local COHERENT system");
823: #else
824: printf("(Back at %s)", *myhost ? myhost : "local UNIX system");
825: #endif /* COHERENT */
826: printf("\n");
827: what = W_NOTHING; /* So console modes set right. */
828: #ifndef NOCSETS
829: language = langsv; /* Restore language */
830: #endif /* NOCSETS */
831: parent_id = (PID_T) 0;
832: #ifdef DYNAMIC
833: if (temp) free(temp);
834: if (lbuf) free(lbuf); /* Free allocated memory */
835: #endif /* DYNAMIC */
836: return(1);
837:
838: } else { /* Inferior reads, prints port input */
839:
840: if (priv_can()) { /* Cancel all privs */
841: printf("?setuid error - fatal\n");
842: doexit(BAD_EXIT,-1);
843: }
844: signal(SIGINT, SIG_IGN); /* In case these haven't been */
845: signal(SIGQUIT, SIG_IGN); /* inherited from above... */
846:
847: shift = 0; /* Initial shift state. */
848: sleep(1); /* Wait for parent's handler setup. */
849: lbp = lbuf; /* Initialize input buffering */
850: lbc = 0;
851: debug(F100,"CONNECT starting port fork","",0);
852: while (1) { /* Fresh read, wait for a character. */
853: #ifdef SUNX25
854: if (network && (nettype == NET_SX25)) {
855: bzero(x25ibuf,sizeof(x25ibuf)) ;
856: if ((ibufl = ttxin(MAXIX25,x25ibuf)) < 0) {
857: if (ibufl == -2) { /* PAD parms changes */
858: write(padpipe[1],padparms,MAXPADPARMS);
859: kill(parent_id,SIGUSR2);
860: } else {
861: if (!quiet)
862: printf("\r\nCommunications disconnect ");
863: kill(parent_id,SIGUSR1);
864: }
865: pause();
866: }
867: if (debses) { /* Debugging output */
868: p = x25ibuf ;
869: while (ibufl--) { c = *p++; conol(dbchr(c)); }
870: } else {
871: if (sosi
872: #ifndef NOCSETS
873: || tcsl != tcsr
874: #endif /* NOCSETS */
875: ) { /* Character at a time */
876: for (i = 1; i < ibufl; i++) {
877: c = x25ibuf[i] & cmask;
878: if (sosi) { /* Handle SI/SO */
879: if (c == SO) {
880: shift = 1;
881: continue;
882: } else if (c == SI) {
883: shift = 0;
884: continue;
885: }
886: if (shift)
887: c |= 0200;
888: }
889: #ifndef NOCSETS
890: #ifndef SKIPESC
891: /* Translate character sets */
892: if (sxo) c = (*sxo)(c); /* From local to intermediate. */
893: if (rxo) c = (*rxo)(c); /* From intermediate to remote. */
894:
895: #else /* Skipping escape sequences... */
896:
897: if (inesc == ES_NORMAL) { /* If not inside escape seq.. */
898: /* Translate character sets */
899: if (sxo) c = (*sxo)(c); /* Local to intermediate. */
900: if (rxo) c = (*rxo)(c); /* Intermediate to remote. */
901: }
902: if (skipesc) chkaes(c); /* Check escape sequence status */
903: #endif /* SKIPESC */
904: #endif /* NOCSETS */
905: c &= cmdmsk; /* Apply command mask. */
906: conoc(c); /* Output to screen */
907: logchar(c); /* and session log */
908: }
909: } else { /* All at once */
910: for (i = 1; i < ibufl; i++)
911: x25ibuf[i] &= (cmask & cmdmsk);
912: conxo(ibufl,x25ibuf);
913: if (seslog) zsoutx(ZSFILE,x25ibuf,ibufl);
914: }
915: }
916: continue;
917:
918: } else { /* Not X.25... */
919: #endif /* SUNX25 */
920: /*
921: Get the next communication line character from our internal buffer.
922: If the buffer is empty, refill it.
923: */
924: if (lbc < 1) { /* Nothing left in input buffer. */
925: lbc = 0; /* Must refresh it. */
926: lbp = lbuf;
927: debug(F100,"CONNECT refill buf","",0);
928: if ((c = ttinc(0)) < 0) { /* Get char from comm line */
929: if (!quiet) { /* Failed... */
930: printf("\r\nCommunications disconnect ");
931: if ( c == -3
932: #ifdef UTEK
933: /* This happens on UTEK if there's no carrier */
934: && errno != EWOULDBLOCK
935: #endif /* UTEK */
936: )
937: perror("\r\nCan't read character");
938: }
939: tthang(); /* Hang up our side. */
940: #ifdef NETCONN
941: if (network && ttnproto == NP_TELNET)
942: tn_init = 0;
943: #endif /* NETCONN */
944: kill(parent_id,SIGUSR1); /* Notify parent */
945: for (;;) pause(); /* and wait to be killed */
946: } else { /* Otherwise, got one character */
947: *lbp++ = c; /* Advance buffer pointer */
948: lbc++; /* and count. */
949: }
950: #ifdef TNCODE
951: /*
952: The following buffering code makes CONNECT mode a lot more efficient,
953: especially on slow workstations. But we can't use it if we're doing TELNET
954: protocol because we won't notice any in-band TELNET commands (tn_doop()
955: wants to read the characters following the IAC itself). So this code will
956: always be used on non-network systems and it will be used on network systems
957: except during a TELNET session.
958: */
959: if (!network || ttnproto != NP_TELNET) {
960: #endif /* TNCODE */
961: /* Now quickly read any more that might have arrived */
962: while ((n = ttchk()) > 0) { /* Any more waiting? */
963: if (n > (LBUFL - lbc)) /* Get them all at once. */
964: n = LBUFL - lbc; /* Don't overflow buffer */
965: if ((n = ttxin(n,(CHAR *)lbp)) > 0) {
966: lbp += n; /* Advance pointer */
967: lbc += n; /* and counter */
968: } else break; /* Break on error */
969: }
970: debug(F101,"connect lbc","",lbc); /* Log how many */
971: /*
972: The following outputs to the console as much as possible in one write to
973: maximize throughput. We can use this only if not doing any character-based
974: operations like TELNET negotiations, Shift-In/Shift/Out, or session
975: debugging. Character-set translation MUST BE ONE-TO-ONE.
976: */
977: if (!sosi && !debses) { /* No SO/SI or debugging... */
978: for (n = 0; n < lbc; n++) {
979: c = lbuf[n] & cmask; /* Apply terminal mask */
980: #ifndef NOCSETS
981: #ifndef SKIPESC
982: if (sxi) c = (*sxi)(c); /* Xlate char sets */
983: if (rxi) c = (*rxi)(c);
984: #else
985: if (inesc == ES_NORMAL) {
986: if (sxi) c = (*sxi)(c);
987: if (rxi) c = (*rxi)(c);
988: }
989: if (skipesc) chkaes(c); /* Esc seq status */
990: #endif /* SKIPESC */
991: #endif /* NOCSETS */
992: lbuf[n] = c & cmdmsk; /* Replace in buffer */
993: if (seslog) logchar(c); /* Log it */
994: }
995: conxo(lbc,lbuf); /* Write out whole buffer */
996: lbc = 0; /* Reset buffer count */
997: continue; /* ...the big while(1) loop */
998: }
999:
1000: #ifdef TNCODE
1001: } /* Closing bracket for Not-TELNET section */
1002: #endif /* TNCODE */
1003: lbp = lbuf; /* Reset buffer pointer to beginning */
1004: }
1005: c = *lbp++; /* Get a character from the buffer. */
1006: lbc--; /* Count it. */
1007: debug(F111,"** PORT",dbchr(c),c);
1008: #ifdef TNCODE
1009: /* Handle TELNET negotiations here */
1010: if (network && ttnproto == NP_TELNET
1011: && ((c & 0xff) == IAC)) {
1012: if ((tx = tn_doop((c & 0xff),duplex)) == 0) {
1013: continue;
1014: } else if (tx == -1) { /* I/O error */
1015: if (!quiet)
1016: printf("\r\nCommunications disconnect ");
1017: kill(parent_id,SIGUSR1); /* Notify parent. */
1018: for (;;) pause(); /* Wait to be killed. */
1019: } else if ((tx == 1) && (!duplex)) { /* ECHO change */
1020: kill(parent_id,SIGUSR2); /* Tell the parent fork */
1021: duplex = 1;
1022: } else if ((tx == 2) && (duplex)) { /* ECHO change */
1023: kill(parent_id,SIGUSR2);
1024: duplex = 0;
1025: } else if (tx == 3) { /* Quoted IAC */
1026: c = 255;
1027: } else continue; /* Negotiation OK, get next char. */
1028: }
1029: #endif /* TNCODE */
1030: if (debses) { /* Output character to screen */
1031: conol(dbchr(c)); /* debugging display */
1032: } else { /* or regular display ... */
1033: c &= cmask; /* Apply Kermit-to-remote mask */
1034: if (sosi) { /* Handle SI/SO */
1035: if (c == SO) { /* Shift Out */
1036: shift = 1;
1037: continue;
1038: } else if (c == SI) { /* Shift In */
1039: shift = 0;
1040: continue;
1041: }
1042: if (shift) c |= 0200;
1043: }
1044: #ifndef NOCSETS
1045: #ifndef SKIPESC
1046: /* Translate character sets */
1047: if (sxi) c = (*sxi)(c);
1048: if (rxi) c = (*rxi)(c);
1049: #else
1050: if (inesc == ES_NORMAL) {
1051: /* Translate character sets */
1052: if (sxi) c = (*sxi)(c);
1053: if (rxi) c = (*rxi)(c);
1054: }
1055: if (skipesc) chkaes(c); /* Adjust escape sequence status */
1056: #endif /* SKIPESC */
1057: #endif /* NOCSETS */
1058: c &= cmdmsk; /* Apply command mask. */
1059: conoc(c); /* Output to screen */
1060: if (seslog) logchar(c); /* Take care of session log */
1061: }
1062: #ifdef SUNX25
1063: }
1064: #endif /* SUNX25 */
1065: }
1066: }
1067: }
1068:
1069:
1070: /* H C O N N E -- Give help message for connect. */
1071:
1072: int
1073: hconne() {
1074: int c;
1075: static char *hlpmsg[] = {
1076: "\r\n 0 (zero) to send a null",
1077: "\r\n B to send a BREAK",
1078: #ifdef UNIX
1079: "\r\n L to send a Long BREAK",
1080: #endif /* UNIX */
1081: #ifdef SUNX25
1082: "\r\n I to send X.25 interrupt packet",
1083: "\r\n R to reset the virtual circuit",
1084: "\r\n H to hangup the connection (clear virtual circuit)",
1085: #else
1086: "\r\n H to hangup and close the connection",
1087: #endif /* SUNX25 */
1088: "\r\n Q to hangup and quit Kermit",
1089: "\r\n S for status",
1090: "\r\n ! to push to local shell",
1091: "\r\n Z to suspend",
1092: "\r\n \\ backslash escape:",
1093: "\r\n \\nnn decimal character code",
1094: "\r\n \\Onnn octal character code",
1095: "\r\n \\Xhh hexadecimal character code",
1096: "\r\n terminate with carriage return.",
1097: "\r\n ? for help",
1098: "\r\n escape character twice to send the escape character",
1099: "\r\n space-bar to resume the CONNECT command\r\n\r\n",
1100: "" };
1101: conol("\r\nPress C to return to ");
1102: conol(*myhost ? myhost : "the C-Kermit prompt");
1103: conol(", or:");
1104: conola(hlpmsg); /* Print the help message. */
1105: conol("Command>"); /* Prompt for command. */
1106: c = congks(0) & 0177; /* Get character, strip any parity. */
1107: /* No key mapping or translation here */
1108: if (c != CMDQ)
1109: conoll("");
1110: return(c); /* Return it. */
1111: }
1112:
1113:
1114: /* D O E S C -- Process an escape character argument */
1115:
1116: VOID
1117: #ifdef CK_ANSIC
1118: doesc(char c)
1119: #else
1120: doesc(c) char c;
1121: #endif /* CK_ANSIC */
1122: /* doesc */ {
1123: CHAR d;
1124:
1125: while (1) {
1126: if (c == escape) { /* Send escape character */
1127: d = dopar(c); ttoc(d); return;
1128: } else /* Or else look it up below. */
1129: if (isupper(c)) c = tolower(c);
1130:
1131: switch(c) {
1132:
1133: case 'c': /* Close connection */
1134: case '\03':
1135: active = 0; conol("\r\n"); return;
1136:
1137: case 'b': /* Send a BREAK signal */
1138: case '\02':
1139: ttsndb(); return;
1140:
1141: #ifdef UNIX
1142: case 'l': /* Send a Long BREAK signal */
1143: ttsndlb(); return;
1144: #endif /* UNIX */
1145:
1146: case 'h': /* Hangup */
1147: case '\010':
1148: #ifdef SUNX25
1149: if (network && (nettype == NET_SX25)) dox25clr = 1;
1150: else
1151: #endif /* SUNX25 */
1152: dohangup = 1; active = 0; conol("\r\nHanging up "); return;
1153:
1154: #ifdef SUNX25
1155: case 'i': /* Send an X.25 interrupt packet */
1156: case '\011':
1157: if (network && (nettype == NET_SX25)) (VOID) x25intr(0);
1158: conol("\r\n"); return;
1159:
1160: case 'r': /* Reset the X.25 virtual circuit */
1161: case '\022':
1162: if (network && (nettype == NET_SX25)) (VOID) x25reset();
1163: conol("\r\n"); return;
1164: #endif /* SUNX25 */
1165:
1166: case 'q':
1167: quitnow = 1; active = 0; conol("\r\n"); return;
1168:
1169: case 's': /* Status */
1170: conol("\r\nConnected thru ");
1171: conol(ttname);
1172: #ifdef SUNX25
1173: if (network && (nettype == NET_SX25))
1174: printf(", Link ID %d, LCN %d",linkid,lcn);
1175: #endif /* SUNX25 */
1176: if (speed >= 0L) {
1177: sprintf(temp,", speed %ld",speed); conol(temp);
1178: }
1179: sprintf(temp,", %d terminal bits",(cmask == 0177) ? 7 : 8);
1180: conol(temp);
1181: if (parity) {
1182: conol(", ");
1183: switch (parity) {
1184: case 'e': conol("even"); break;
1185: case 'o': conol("odd"); break;
1186: case 's': conol("space"); break;
1187: case 'm': conol("mark"); break;
1188: }
1189: conol(" parity");
1190: }
1191: if (seslog) {
1192: conol(", logging to "); conol(sesfil);
1193: }
1194: conoll(""); return;
1195:
1196: case '?': /* Help */
1197: c = hconne(); continue;
1198:
1199: case '0': /* Send a null */
1200: c = '\0'; d = dopar(c); ttoc(d); return;
1201:
1202: #ifndef NOPUSH
1203: case 'z': case '\032': /* Suspend */
1204: stptrap(0); return;
1205:
1206: case '@': /* Start inferior command processor */
1207: case '!':
1208: conres(); /* Put console back to normal */
1209: zshcmd(""); /* Fork a shell. */
1210: if (conbin(escape) < 0) {
1211: printf("Error resuming CONNECT session\n");
1212: active = 0;
1213: }
1214: return;
1215: #endif /* NOPUSH */
1216:
1217: case SP: /* Space, ignore */
1218: return;
1219:
1220: default: /* Other */
1221: if (c == CMDQ) { /* Backslash escape */
1222: int x;
1223: kbp = kbuf;
1224: *kbp++ = c;
1225: while (((c = (congks(0) & cmdmsk)) != '\r') && (c != '\n'))
1226: *kbp++ = c;
1227: *kbp = NUL; kbp = kbuf;
1228: x = xxesc(&kbp); /* Interpret it */
1229: if (x >= 0) { /* No key mapping here */
1230: c = dopar(x);
1231: ttoc(c);
1232: return;
1233: } else { /* Invalid backslash code. */
1234: conoc(BEL);
1235: return;
1236: }
1237: }
1238: conoc(BEL); return; /* Invalid esc arg, beep */
1239: }
1240: }
1241: }
1242:
1243: VOID
1244: #ifdef CK_ANSIC
1245: logchar(char c)
1246: #else
1247: logchar(c) char c;
1248: #endif /* CK_ANSIC */
1249: /* logchar */ { /* Log character c to session log */
1250: if (seslog)
1251: if ((sessft != 0) ||
1252: (c != '\r' &&
1253: c != '\0' &&
1254: c != XON &&
1255: c != XOFF))
1256: if (zchout(ZSFILE,c) < 0) {
1257: conoll("");
1258: conoll("ERROR WRITING SESSION LOG, LOG CLOSED!");
1259: seslog = 0;
1260: }
1261: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.