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