1 #include "ckcsym.h"
2 
3 #ifndef NOICP
4 #ifndef NOSCRIPT
5 char *loginv = "Script Command, 9.0.032, 16 Oct 2009";
6 
7 /*  C K U S C R  --  expect-send script implementation  */
8 
9 /*
10   Copyright (C) 1985, 2013,
11     Trustees of Columbia University in the City of New York.
12     All rights reserved.  See the C-Kermit COPYING.TXT file or the
13     copyright text in the ckcmai.c module for disclaimer and permissions.
14 
15   Original (version 1, 1985) author: Herm Fischer, Encino, CA.
16   Contributed to Columbia University in 1985 for inclusion in C-Kermit 4.0.
17   Maintained since 1985 by Frank da Cruz, fdc@columbia.edu.
18 
19   The module takes a UUCP-style script of the "expect send [expect send] ..."
20   format.  It is intended to operate similarly to the way the common
21   UUCP L.sys login entries work.  Conditional responses are supported:
22   expect[-send-expect[...]], as with UUCP.  The send keyword EOT sends a
23   Control-d, and the keyword BREAK sends a break.  Letters prefixed
24   by '~' are '~b' backspace, '~s' space, '~n' linefeed, '~r' return, '~x' xon,
25   '~t' tab, '~q' ? (not allowed on kermit command lines), '~' ~, '~'',
26   '~"', '~c' don't append return, '~o[o[o]]' octal character.  As with
27   some uucp systems, sent strings are followed by ~r (not ~n) unless they
28   end with ~c. Null expect strings (e.g., ~0 or --) cause a short
29   delay, and are useful for sending sequences requiring slight pauses.
30 
31   This module calls externally defined system-dependent functions for
32   communications i/o, as defined in ckcplm.txt, the C-Kermit Program Logic
33   Manual, and thus should be portable to all systems that implement those
34   functions, and where alarm() and signal() work as they do in UNIX.
35 */
36 #include "ckcdeb.h"
37 #include <signal.h>
38 #ifdef NT
39 #include <setjmpex.h>
40 #else /* NT */
41 #include <setjmp.h>
42 #endif /* NT */
43 #include "ckcasc.h"
44 #include "ckcker.h"
45 #include "ckuusr.h"
46 #include "ckcnet.h"
47 #include "ckcsig.h"
48 
49 _PROTOTYP( VOID flushi, (void) );
50 _PROTOTYP( static VOID myflsh, (void) );
51 _PROTOTYP( static int sequenc, (void) );
52 _PROTOTYP( static VOID recvseq, (void) );
53 _PROTOTYP( static int outseq, (void) );
54 
55 #ifdef MAC
56 #define signal msignal
57 #define SIGTYP long
58 #define alarm malarm
59 #define SIG_IGN 0
60 #define SIGALRM 1
61 #define SIGINT  2
62 SIGTYP (*msignal(int type, SIGTYP (*func)(int)))(int);
63 #endif /* MAC */
64 
65 #ifdef AMIGA
66 #define signal asignal
67 #define alarm aalarm
68 #define SIGALRM (_NUMSIG+1)
69 #define SIGTYP void
70 SIGTYP (*asignal(int type, SIGTYP (*func)(int)))(int);
71 unsigned aalarm(unsigned);
72 #endif /* AMIGA */
73 
74 #ifdef STRATUS
75 /* VOS doesn't have alarm(), but it does have some things we can work with. */
76 /* however, we have to catch all the signals in one place to do this, so    */
77 /* we intercept the signal() routine and call it from our own replacement.  */
78 #define signal vsignal
79 #define alarm valarm
80 SIGTYP (*vsignal(int type, SIGTYP (*func)(int)))(int);
81 int valarm(int interval);
82 #endif /* STRATUS */
83 
84 extern int sessft;
85 extern int local, flow, seslog, mdmtyp, msgflg, duplex, backgrd, secho, quiet;
86 extern int network, nettype, ttnproto;
87 extern long speed;
88 extern char ttname[];
89 
90 #ifdef NTSIG
91 extern int TlsIndex;
92 #endif /* NTSIG */
93 #ifdef IKSD
94 extern int inserver;
95 #endif /* IKSD */
96 
97 static int is_tn = 0;			/* Do Telnet negotiations */
98 
99 #ifndef NOSPL
100 #ifdef DCMDBUF
101 extern struct cmdptr *cmdstk;
102 #else
103 extern struct cmdptr cmdstk[];
104 #endif /* DCMDBUF */
105 extern int techo, cmdlvl;
106 extern int mecho;
107 #endif /* NOSPL */
108 
109 static int scr_echo;			/* Whether to echo script commands */
110 
111 static int exp_alrm = 15;		/* Time to wait for expect string */
112 #define SND_ALRM 15			/* Time to allow for sending string */
113 #define NULL_EXP 2			/* Time to pause on null expect strg*/
114 #define DEL_MSEC 300			/* Milliseconds to pause on ~d */
115 
116 #define SBUFL 512
117 static char seq_buf[SBUFL+2], *s;	/* expect-send sequence buffer */
118 static int got_it, no_cr;
119 
120 /*  Connect state parent/child communication signal handlers */
121 
122 #ifdef COMMENT
123 #ifdef CK_POSIX_SIG
124 static sigjmp_buf alrmrng;
125 #else
126 static jmp_buf alrmrng;
127 #endif /* CK_POSIX_SIG */
128 #else
129 static ckjmpbuf alrmrng;
130 #endif /* COMMENT */
131 
132 static SIGTYP
133 #ifdef CK_ANSIC
scrtime(int foo)134 scrtime(int foo)			/* modem read failure handler, */
135 #else
136 scrtime(foo) int foo;			/* Alarm handler */
137 #endif /* CK_ANSIC */
138 /* scrtime */ {
139 
140 #ifdef BEBOX
141 #ifdef BE_DR_7
142     alarm_expired();
143 #endif /* BE_DR_7 */
144 #endif /* BEBOX */
145 #ifdef NTSIG
146     if (foo == SIGALRM)
147       PostAlarmSigSem();
148     else
149       PostCtrlCSem();
150 #else /* NTSIG */
151 #ifdef NT
152     cklongjmp(ckjaddr(alrmrng),1);
153 #else /* NT */
154     cklongjmp(alrmrng,1);
155 #endif /* NT */
156 #endif /* NTSIG */
157     SIGRETURN;
158 }
159 
160 /*
161  Sequence interpreter -- pick up next sequence from command string,
162  decode escapes and place into seq_buf.
163 
164  If string contains a ~d (delay) then sequenc() returns a 1 expecting
165  to be called again after the ~d executes.
166 */
167 static int
sequenc()168 sequenc() {
169     int i;
170     char c, oct_char;
171 
172     no_cr = 0;				/* output needs cr appended */
173     for (i = 0; i < SBUFL; ) {
174 	if (*s == '\0' || *s == '-' || isspace(*s) ) { /* done */
175 	    seq_buf[i] = '\0';
176 	    return(0) ;
177 	}
178 	if (*s == '~') {		/* escape character */
179 	    s++;
180 	    switch (c = *s) {
181 		case 'n':  seq_buf[i++] = LF; break;
182 		case 'r':  seq_buf[i++] = CR; break;
183 		case 't':  seq_buf[i++] = '\t'; break;
184 		case 'b':  seq_buf[i++] = '\b'; break;
185 		case 'q':  seq_buf[i++] = '?';  break;
186 #ifdef COMMENT
187 /* The default case should catch these now... */
188 		case '~':  seq_buf[i++] = '~';  break;
189 		case '-':  seq_buf[i++] = '-';  break;
190 #endif /* COMMENT */
191 		case '\'': seq_buf[i++] = '\''; break;
192 		case '\"': seq_buf[i++] = '\"'; break;
193 		case 's':  seq_buf[i++] = ' ';  break;
194 		case 'x':  seq_buf[i++] = '\021'; break;
195 		case 'c':  no_cr = 1; break;
196 		case 'd': {			/* send what we have & then */
197 		    seq_buf[i] = '\0';		/* expect to send rest after */
198 		    no_cr = 1;			/* sender delays a little */
199 		    s++;
200 		    return(1);
201 		}
202 		case 'w': {			/* wait count */
203 		    exp_alrm = 15;		/* default to 15 sec */
204 		    if (isdigit(*(s+1))) {
205 			s++;
206 			exp_alrm = *s & 15;
207 			if (isdigit(*(s+1)) ) {
208 			    s++;
209 			    exp_alrm = exp_alrm * 10 + (*s & 15);
210 			}
211 		    }
212 		    break;
213 		}
214 		default:
215 		    if ( isdigit(c) ) {	    	/* octal character */
216 		    	oct_char = (char) (c & 7); /* most significant digit */
217 			if (isdigit( *(s+1) ) ) {
218 			    s++;
219 			    oct_char = (char) ((oct_char<<3) | ( *s & 7 ));
220 			    if (isdigit( *(s+1) ) ) {
221 				s++;
222 			    	oct_char = (char) ((oct_char<<3) | ( *s & 7 ));
223 			    }
224 			}
225 			seq_buf[i++] = oct_char;
226 			break;
227 		    } else seq_buf[i++] = *s; /* Treat ~ as quote */
228 	      }
229 	} else seq_buf[i++] = *s;	/* Plain old character */
230 	s++;
231     }
232     seq_buf[i] = '\0';
233     return(0);				/* end of space, return anyway */
234 }
235 
236 
237 /* Output buffering for "recvseq" and "flushi" */
238 
239 #define	MAXBURST 256		/* maximum size of input burst */
240 static CHAR conbuf[MAXBURST];	/* buffer to hold output for console */
241 static int concnt = 0;		/* number of characters buffered */
242 static CHAR sesbuf[MAXBURST];	/* buffer to hold output for session log */
243 static int sescnt = 0;		/* number of characters buffered */
244 
245 static VOID
myflsh()246 myflsh() {
247     if (concnt > 0) {
248 	conxo(concnt, (char *) conbuf);
249 	concnt = 0;
250     }
251     if (sescnt > 0) {
252         logstr((char *) sesbuf, sescnt);
253 	sescnt = 0;
254     }
255 }
256 
257 /* these variables are used to pass data between the recvseq() */
258 /* and the dorseq().  They are necessary because in some versions */
259 /* dorseq() is executed in a separate thread and data cannot be */
260 /* passed by parameter. */
261 
262 static char *rseqe, * rseqgot, * rseqtrace ;
263 static int rseql;
264 
265 static SIGTYP
266 #ifdef CK_ANSIC
dorseq(void * threadinfo)267 dorseq(void * threadinfo)
268 #else /* CK_ANSIC */
269 dorseq(threadinfo) VOID * threadinfo;
270 #endif /* CK_ANSIC */
271 /* dorseq */ {
272     int i, x;
273     int burst = 0;			/* chars remaining in input burst */
274 
275 #ifdef NTSIG
276     setint();
277     if (threadinfo) {			/* Thread local storage... */
278 	TlsSetValue(TlsIndex,threadinfo);
279     }
280 #endif /* NTSIG */
281 #ifdef CK_LOGIN
282 #ifdef NT
283 #ifdef IKSD
284     if (inserver)
285       setntcreds();
286 #endif /* IKSD */
287 #endif /* NT */
288 #endif /* CK_LOGIN */
289 
290     while (!got_it) {
291 	for (i = 0; i < rseql-1; i++) rseqgot[i] = rseqgot[i+1];
292 	x = ttinc(0);			/* Read a character */
293 	debug(F101,"recvseq","",x);
294 	if (x < 0) {
295 #ifdef NTSIG
296 	    ckThreadEnd(threadinfo);
297 #endif /* NTSIG */
298 	    SIGRETURN;			/* Check for error */
299 	}
300 #ifdef NETCONN
301 #ifdef TNCODE
302 /* Check for telnet protocol negotiation */
303 	if (((x & 0xff) == IAC) && is_tn) { /* Telnet negotiation */
304 	    myflsh();
305 	    burst = 0;
306 	    switch (tn_doop((CHAR)(x & 0xff),duplex,ttinc)) {
307 	      case 2: duplex = 0; continue;
308 	      case 1: duplex = 1;
309 	      default: continue;
310 	    }
311 	}
312 #endif /* TNCODE */
313 #endif /* NETCONN */
314 	rseqgot[rseql-1] = (char) (x & 0x7f); /* Got a character */
315 	burst--;			/* One less waiting */
316 	if (scr_echo) conbuf[concnt++] = rseqgot[rseql-1]; /* Buffer it */
317 	if (seslog)			/* Log it in session log */
318 #ifdef UNIX
319 	  if (sessft != 0 || rseqgot[rseql-1] != '\r')
320 #else
321 #ifdef OSK
322 	    if (sessft != 0 || rseqgot[rseql-1] != '\012')
323 #endif /* OSK */
324 #endif /* UNIX */
325 	      if (rseqgot[rseql-1])	/* Filter out NULs */
326 		sesbuf[sescnt++] = rseqgot[rseql-1];
327 	if ((int)strlen(rseqtrace) < SBUFL-2 )
328 	  strcat(rseqtrace,dbchr(rseqgot[rseql-1]));
329 	got_it = (!strncmp(rseqe, rseqgot, rseql));
330 	if (burst <= 0) {		/* Flush buffered output */
331 	    myflsh();
332 	    if ((burst = ttchk()) < 0) { /* Get size of next input burst */
333 #ifdef NTSIG
334 		ckThreadEnd(threadinfo);
335 #endif /* NTSIG */
336 		SIGRETURN;
337 	    }
338 	    /* prevent overflow of "conbuf" and "sesbuf" */
339 	    if (burst > MAXBURST)
340 	      burst = MAXBURST;
341 	}
342     }
343 #ifdef NTSIG
344     ckThreadEnd(threadinfo);
345 #endif /* NTSIG */
346     SIGRETURN;
347 }
348 
349 static SIGTYP
350 #ifdef CK_ANSIC
failrseq(void * threadinfo)351 failrseq(void * threadinfo)
352 #else /* CK_ANSIC */
353 failrseq(threadinfo) VOID * threadinfo;
354 #endif /* CK_ANSIC */
355 /* failrseq */ {
356      got_it = 0;			/* Timed out here */
357      SIGRETURN;
358 }
359 
360 /*
361   Receive sequence -- see if expected response comes,
362   return success (or failure) in got_it.
363 */
364 static VOID
recvseq()365 recvseq() {
366     char *e, got[7], trace[SBUFL];
367     int i, l;
368 
369     sequenc();
370     l = (int)strlen(e=seq_buf);		/* no more than 7 chars allowed */
371     if (l > 7) {
372 	e += l-7;
373 	l = 7;
374     }
375     tlog(F111,"expecting sequence",e,(long) l);
376     if (l == 0) {			/* null sequence, delay a little */
377 	sleep (NULL_EXP);
378 	got_it = 1;
379 	tlog(F100,"got it (null sequence)","",0L);
380 	return;
381     }
382     *trace = '\0';
383     for (i = 0; i < 7; i++) got[i]='\0';
384 
385     rseqtrace = trace;
386     rseqe = e;
387     rseqgot = got;
388     rseql = l;
389 
390     alrm_execute(ckjaddr(alrmrng), exp_alrm, scrtime, dorseq, failrseq);
391 
392     tlog(F110,"received sequence: ",trace,0L);
393     tlog(F101,"returning with got-it code","",(long) got_it);
394     myflsh();				/* Flush buffered output */
395     return;
396 }
397 
398 /*
399  Output A Sequence starting at pointer s,
400  return 0 if okay,
401  1 if failed to read (modem hangup or whatever)
402 */
403 static int oseqret = 0;			/* Return code for outseq */
404 					/* Out here to prevent clobbering */
405 					/* by longjmp. */
406 
407 static SIGTYP
408 #ifdef CK_ANSIC
dooseq(void * threadinfo)409 dooseq(void * threadinfo)
410 #else /* CK_ANSIC */
411 dooseq(threadinfo) VOID * threadinfo;
412 #endif /* CK_ANSIC */
413 {
414     int l;
415     char *sb;
416 #ifdef TCPSOCKET
417     extern int tn_nlm, tn_b_nlm;
418 #endif /* TCPSOCKET */
419 
420 #ifdef NTSIG
421     setint();
422     if (threadinfo) {			/* Thread local storage... */
423 	TlsSetValue(TlsIndex,threadinfo);
424     }
425 #endif /* NTSIG */
426 #ifdef CK_LOGIN
427 #ifdef NT
428 #ifdef IKSD
429     if (inserver)
430       setntcreds();
431 #endif /* IKSD */
432 #endif /* NT */
433 #endif /* CK_LOGIN */
434 
435     l = (int)strlen(seq_buf);
436     tlog(F111,"sending sequence ",seq_buf,(long) l);
437 
438     if (!strcmp(seq_buf,"EOT")) {
439 	ttoc(dopar('\004'));
440 	if (scr_echo) conol("<EOT>");
441 	if (seslog && duplex)
442             logstr("<EOT>",5);
443     } else if (!strcmp(seq_buf,"BREAK") ||
444 	       !strcmp(seq_buf,"\\b") ||
445 	       !strcmp(seq_buf,"\\B")) {
446 	ttsndb();
447 	if (scr_echo) conol("<BREAK>");
448 	if (seslog)
449 	  logstr("{BREAK}",7);
450     } else {
451 	if (l > 0) {
452 	    for ( sb = seq_buf; *sb; sb++)
453 	      *sb = dopar(*sb);	/* add parity */
454 	    ttol((CHAR *)seq_buf,l); /* send it */
455 	    if (scr_echo && duplex) {
456 #ifndef NOLOCAL
457 #ifdef OS2
458 		{			/* Echo to emulator */
459 		    char *s = seq_buf;
460 		    while (*s) {
461 			scriptwrtbuf((USHORT)*s);
462 		    }
463 		}
464 #endif /* OS2 */
465 #endif /* NOLOCAL */
466 		conxo(l,seq_buf);
467 	    }
468 	    if (seslog && duplex) /* log it */
469 	      logstr(seq_buf,strlen(seq_buf));
470 	}
471 	if (!no_cr) {
472 	    ttoc( dopar(CR) );
473 #ifdef TCPSOCKET
474 	    if (is_tn) {
475 		if (!TELOPT_ME(TELOPT_BINARY) && tn_nlm != TNL_CR)
476 		  ttoc((char)((tn_nlm == TNL_CRLF) ?
477 			      dopar(LF) : dopar(NUL)));
478 		else if (TELOPT_ME(TELOPT_BINARY) &&
479 			 (tn_b_nlm == TNL_CRLF || tn_b_nlm == TNL_CRNUL))
480 		  ttoc((char)((tn_b_nlm == TNL_CRLF) ?
481 			      dopar(LF) : dopar(NUL)));
482 	    }
483 #endif /* TCPSOCKET */
484 	    if (seslog && duplex)
485 	      logchar(dopar(CR));
486 	}
487     }
488 #ifdef NTSIG
489     ckThreadEnd(threadinfo);
490 #endif /* NTSIG */
491     SIGRETURN;
492 }
493 
494 SIGTYP
495 #ifdef CK_ANSIC
failoseq(void * threadinfo)496 failoseq(void * threadinfo)
497 #else /* CK_ANSIC */
498 failoseq(threadinfo) VOID * threadinfo;
499 #endif /* CK_ANSIC */
500 /* failoseq */ {
501      oseqret = -1;		/* else -- alarm rang */
502      SIGRETURN;
503 }
504 
505 static int
outseq()506 outseq() {
507     int delay;
508 
509     oseqret = 0;			/* Initialize return code */
510     while(1) {
511 	delay = sequenc();
512 	alrm_execute( ckjaddr(alrmrng), SND_ALRM, scrtime, dooseq, failoseq ) ;
513 
514 	if (!delay)
515 	  return(oseqret);
516 #ifndef MAC
517 	msleep(DEL_MSEC);		/* delay, loop to next send */
518 #endif /* MAC */
519     }
520 }
521 
522 
523 /*  L O G I N  --  (historical misnomer) Execute the SCRIPT command */
524 
525 int
dologin(cmdstr)526 dologin(cmdstr) char *cmdstr; {
527 
528 #ifdef OS2
529 #ifdef NT
530     SIGTYP (* savealm)(int);		/* Save incoming alarm function */
531 #else /* NT */
532     SIGTYP (* volatile savealm)(int);	/* Save incoming alarm function */
533 #endif /* NT */
534 #else /* OS2 */
535     SIGTYP (*savealm)();		/* Save incoming alarm function */
536 #endif /* OS2 */
537     char *e;
538 
539     s = cmdstr;				/* Make global to this module */
540 
541     tlog(F100,loginv,"",0L);
542 
543     if (speed < 0L) speed = ttgspd();
544     if (ttopen(ttname,&local,mdmtyp,0) < 0) {
545 	ckmakmsg(seq_buf,SBUFL,"Sorry, can't open ",ttname,NULL,NULL);
546 	perror(seq_buf);
547 	return(0);
548     }
549     /* Whether to echo script commands ... */
550     scr_echo = (!quiet && !backgrd && secho);
551 #ifndef NOSPL
552     if (scr_echo && cmdlvl > 1) {
553 	if (cmdstk[cmdlvl].src == CMD_TF)
554 	  scr_echo = techo;
555 	if (cmdstk[cmdlvl].src == CMD_MD)
556 	  scr_echo = mecho;
557     }
558 #endif /* NOSPL */
559     if (scr_echo) {
560 #ifdef NETCONN
561 	if (network)
562 	  printf("Executing SCRIPT to host %s.\n",ttname);
563 	else
564 #endif /* NETCONN */
565 	  printf("Executing SCRIPT through %s, speed %ld.\n",ttname,speed);
566     }
567 #ifdef TNCODE
568     /* TELNET input must be scanned for IAC */
569     is_tn = (local && network && IS_TELNET()) ||
570 	    (!local && sstelnet);
571 #endif /* TNCODE */
572 
573     *seq_buf = 0;
574     for (e = s; *e; e++) ckstrncat(seq_buf,dbchr(*e),SBUFL);
575 #ifdef COMMENT
576 /* Skip this because it tends to contain a password... */
577     if (scr_echo) printf("SCRIPT string: %s\n",seq_buf);
578 #endif /* COMMENT */
579     tlog(F110,"SCRIPT string: ",seq_buf, 0L);
580 
581 /* Condition console terminal and communication line... */
582 
583     if (ttvt(speed,flow) < 0) {
584 	printf("Sorry, Can't condition communication line\n");
585 	return(0);
586     }
587     /* Save initial timer interrupt value */
588     savealm = signal(SIGALRM,SIG_IGN);
589 
590     flushi();				/* Flush stale input */
591 
592 /* start expect - send sequence */
593 
594     while (*s) {			/* While not done with buffer */
595 
596 	while (*s && isspace(*s)) s++;	/* Skip over separating whitespaces */
597 					/* Gather up expect sequence */
598 	got_it = 0;
599 	recvseq();
600 
601 	while (!got_it) {		/* Have it yet? */
602 	    if (*s++ != '-')		/* No, is there a conditional send? */
603 	      goto failret;		/* No, return failure */
604 	    flushi();			/* Yes, flush out input buffer */
605 	    if (outseq())		/* If unable to send, */
606 	      goto failret;		/* return failure. */
607 	    if (*s++ != '-')		/* If no conditional response here, */
608 	      goto failret;		/* return failure. */
609 	    recvseq();			/* All OK, read response from host. */
610 	}				/* Loop back and check got_it */
611 
612 	while (*s && !isspace(*s++) ) ;	/* Skip over conditionals */
613 	while (*s && isspace(*s)) s++;	/* Skip over separating whitespaces */
614 	flushi();			/* Flush */
615 	if (*s) if (outseq()) goto failret; /* If any */
616     }
617     signal(SIGALRM,savealm);
618     if (scr_echo) printf("Script successful.\n");
619     tlog(F100,"Script successful.","",0L);
620     return(1);
621 
622 failret:
623     signal(SIGALRM,savealm);
624     if (scr_echo) printf("Sorry, script failed\n");
625     tlog(F100,"Script failed","",0L);
626     return(0);
627 }
628 
629 /*  F L U S H I  --  Flush, but log, SCRIPT input buffer  */
630 
631 VOID
flushi()632 flushi() {
633     int n, x;
634     if (
635 	seslog				/* Logging session? */
636 	|| scr_echo			/* Or console echoing? */
637 #ifdef NETCONN
638 #ifdef TNCODE
639 	/* TELNET input must be scanned for IAC */
640 	|| is_tn
641 #endif /* TNCODE */
642 #endif /* NETCONN */
643 	) {
644         if ((n = ttchk()) < 0)		/* Yes, anything in buffer? */
645 	  return;
646 	if (n > MAXBURST) n = MAXBURST;	/* Make sure not too much, */
647 	myflsh();			/* and that buffers are empty. */
648 	while (n-- > 0) {
649   	    x = ttinc(0);		/* Collect a character */
650 #ifdef NETCONN
651 #ifdef TNCODE
652 /* Check for telnet protocol negotiation */
653   	    if (is_tn && ((x & 0xff) == IAC) ) {
654 		myflsh();		/* Sync output */
655   		switch (tn_doop((CHAR)(x & 0xff),duplex,ttinc)) {
656   		  case 2: duplex = 0; break;
657   		  case 1: duplex = 1;
658 		  default: break;
659 		}
660 
661 		/* Recalculate flush count */
662 		if ((n = ttchk()) < 0)
663 		  return;
664 		if (n > MAXBURST) n = MAXBURST;
665   		continue;
666   	    }
667 #endif /* TNCODE */
668 #endif /* NETCONN */
669 	    if (scr_echo) conbuf[concnt++] = (CHAR) x; /* buffer for console */
670 	    if (seslog)
671 #ifdef UNIX
672 	      if (sessft != 0 || x != '\r')
673 #else
674 #ifdef OSK
675 	      if (sessft != 0 || x != '\012')
676 #endif /* OSK */
677 #endif /* UNIX */
678 		sesbuf[sescnt++] = (CHAR) x; /* buffer for session log */
679   	}
680 	myflsh();
681     } else ttflui();			/* Otherwise just flush. */
682 }
683 
684 #else /* NOSCRIPT */
685 char *loginv = "Script Command Disabled";
686 #endif /* NOSCRIPT */
687 #endif /* NOICP */
688