|
|
1.1 root 1: /*
2: * Copyright 1984, 1985 by the Regents of the University of
3: * California and by Gregory Glenn Minshall.
4: *
5: * Permission to use, copy, modify, and distribute these
6: * programs and their documentation for any purpose and
7: * without fee is hereby granted, provided that this
8: * copyright and permission appear on all copies and
9: * supporting documentation, the name of the Regents of
10: * the University of California not be used in advertising
11: * or publicity pertaining to distribution of the programs
12: * without specific prior permission, and notice be given in
13: * supporting documentation that copying and distribution is
14: * by permission of the Regents of the University of California
15: * and by Gregory Glenn Minshall. Neither the Regents of the
16: * University of California nor Gregory Glenn Minshall make
17: * representations about the suitability of this software
18: * for any purpose. It is provided "as is" without
19: * express or implied warranty.
20: */
21:
22:
23: /* this exists to patch over DataFromNetwork until it is ready */
24:
25: #include <signal.h>
26: #include <sgtty.h>
27: #include <stdio.h>
28: #include <curses.h>
29:
30: #include "ascebc.h"
31: #include "3270.h"
32: #include "screen.h"
33:
34: #ifndef lint
35: static char sccsid[] = "@(#)datastream.c 2.12\t12/16/85";
36: #endif /* lint */
37:
38: void EmptyTerminal();
39:
40: #define CorrectTerminalCursor() ((TransparentClock == OutputClock)? terminalCursorAddress:UnLocked? CursorAddress: HighestScreen())
41:
42: #define SetHighestLowest(position) { \
43: if (position < Lowest) { \
44: Lowest = position; \
45: } \
46: if (position > Highest) { \
47: Highest = position; \
48: } \
49: }
50:
51: extern char ebcasc[NEBCASC][NEBC]; /* translate table */
52:
53: static int terminalCursorAddress; /* where the cursor is on term */
54: static int screenInitd; /* the screen has been initialized */
55: static int MAX_CHANGES_BEFORE_POLL; /* how many characters before looking */
56: /* at terminal and net again */
57:
58: static int needToRing = 0; /* need to ring terinal bell */
59: static char *bellSequence = "\07"; /* bell sequence (may be replaced by
60: * VB during initialization)
61: */
62: static char *KS, *KE; /* Turn on and off keyboard */
63:
64: static char Blanks[sizeof Terminal]; /* lots of blanks */
65:
66: /* some globals */
67:
68: int OutputClock; /* what time it is */
69: int TransparentClock; /* time we were last in transparent */
70:
71:
72: /* StartScreen - called to initialize the screen, etc. */
73:
74: StartScreen()
75: {
76: int save;
77: struct sgttyb ourttyb;
78: static int speeds[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800,
79: 2400, 4800, 9600 };
80: static char KSEbuffer[2050];
81: char *lotsofspace = KSEbuffer, *tgetstr();
82:
83: if (!screenInitd) {
84: ioctl(1, TIOCGETP, (char *) &ourttyb);
85: if ((ourttyb.sg_ospeed < 0) || (ourttyb.sg_ospeed > B9600)) {
86: MAX_CHANGES_BEFORE_POLL = 1920;
87: } else {
88: MAX_CHANGES_BEFORE_POLL = speeds[ourttyb.sg_ospeed]/10;
89: if (MAX_CHANGES_BEFORE_POLL < 40) {
90: MAX_CHANGES_BEFORE_POLL = 40;
91: }
92: }
93: save = mode(0);
94: initscr(); /* start up curses */
95: nonl();
96: /* the problem is that curses catches SIGTSTP to
97: * be nice, but it messes us up.
98: */
99: signal(SIGTSTP, SIG_DFL);
100: KS = tgetstr("ks", &lotsofspace);
101: KE = tgetstr("ke", &lotsofspace);
102: if (KS) {
103: StringToTerminal(KS);
104: }
105: DoARefresh();
106: (void) mode(save);
107: if (VB && *VB) {
108: bellSequence = VB; /* use visual bell */
109: }
110: screenInitd = 1;
111: }
112: }
113:
114:
115:
116: /* Stop3270 - called when we are going away... */
117:
118: Stop3270(doNewLine)
119: int doNewLine;
120: {
121: if (screenInitd) {
122: int save;
123:
124: move(NUMBERLINES-1, 1);
125: DoARefresh();
126: if (KE) {
127: StringToTerminal(KE);
128: }
129: if (doNewLine) {
130: StringToTerminal("\r\n");
131: }
132: EmptyTerminal();
133: save = mode(0);
134: endwin();
135: (void) mode(save);
136: }
137: }
138:
139:
140: /* ConnectScreen - called to reconnect to the screen */
141:
142: ConnectScreen()
143: {
144: if (screenInitd) {
145: if (KS) {
146: StringToTerminal(KS);
147: }
148: RefreshScreen();
149: TryToSend();
150: }
151: }
152:
153: /* RefreshScreen - called to cause the screen to be refreshed */
154:
155: RefreshScreen()
156: {
157: clearok(curscr, TRUE);
158: TryToSend();
159: }
160:
161:
162: /* Clear3270 - called to clear the screen */
163:
164: Clear3270()
165: {
166: bzero((char *)Host, sizeof(Host));
167: DeleteAllFields(); /* get rid of all fields */
168: BufferAddress = SetBufferAddress(0,0);
169: CursorAddress = SetBufferAddress(0,0);
170: Lowest = LowestScreen();
171: Highest = HighestScreen();
172: }
173:
174: /* LocalClear3270() - clear the whole ball of wax, cheaply */
175:
176: LocalClear3270()
177: {
178: outputPurge(); /* flush all data to terminal */
179: clear(); /* clear in curses */
180: bzero((char *)Terminal, sizeof Terminal);
181: Clear3270(); /* clear host part */
182: Lowest = HighestScreen()+1; /* everything in sync... */
183: Highest = LowestScreen()+1;
184: }
185:
186: /* OurExitString - designed to keep us from going through infinite recursion */
187:
188: OurExitString(file, string, value)
189: FILE *file;
190: char *string;
191: int value;
192: {
193: static int recursion = 0;
194:
195: if (!recursion) {
196: recursion = 1;
197: ExitString(file, string, value);
198: }
199: }
200:
201:
202: RingBell()
203: {
204: needToRing = 1;
205: }
206:
207: /* AddHost - called to add a character to the buffer.
208: * We use a macro in this module, since we call it so
209: * often from loops.
210: *
211: * NOTE: It is a macro, so don't go around using AddHost(p, *c++), or
212: * anything similar. (I don't define any temporary variables, again
213: * just for the speed.)
214: */
215: AddHost(position, character)
216: int position;
217: char character;
218: {
219: # define AddHostA(p,c) \
220: { \
221: if (IsStartField(p)) { \
222: DeleteField(p); \
223: SetHighestLowest(p); \
224: } \
225: SetHost(p, c); \
226: }
227: # define AddHost(p,c) \
228: { \
229: AddHostA(p,c); \
230: if ((c != GetTerminal(p)) || TermIsStartField(p)) { \
231: SetHighestLowest(p); \
232: } \
233: } /* end of macro of AddHost */
234:
235: AddHost(position, character);
236: }
237:
238: /* DoARefresh */
239:
240: static
241: DoARefresh()
242: {
243: if (ERR == refresh()) {
244: OurExitString(stderr, "ERR from refresh\n", 1);
245: }
246: }
247:
248: /* TryToSend - send data out to user's terminal */
249:
250: static
251: TryToSend()
252: {
253: register int pointer;
254: register int c;
255: register int fieldattr;
256: register int changes;
257:
258: static int inHighlightMode = 0;
259: extern int HaveInput; /* 1 if there is input to check out */
260:
261: # define SetHighlightMode(p) { \
262: if (!IsStartField(p) && IsHighlightedAttr(fieldattr)) { \
263: if (!inHighlightMode) { \
264: inHighlightMode = 1; \
265: standout(); \
266: } \
267: } else { \
268: if (inHighlightMode) { \
269: inHighlightMode = 0; \
270: standend(); \
271: } \
272: } \
273: }
274:
275: # define DoCharacterAt(c,p) { \
276: SetTerminal(p, c); \
277: if (p != HighestScreen()) { \
278: c = TerminalCharacterAttr(ebcasc[0][c&0xff], p, \
279: fieldattr); \
280: if (terminalCursorAddress != p) { \
281: if (ERR == mvaddch(ScreenLine(p), \
282: ScreenLineOffset(p), c)) {\
283: char foo[100]; \
284: \
285: sprintf(foo, "ERR from mvaddch at %d (%d, %d)\n", \
286: p, ScreenLine(p), ScreenLineOffset(p)); \
287: OurExitString(stderr, foo, 1); \
288: } \
289: } else { \
290: if (ERR == addch(c)) {\
291: char foo[100]; \
292: \
293: sprintf(foo, "ERR from addch at %d (%d, %d)\n", \
294: p, ScreenLine(p), ScreenLineOffset(p)); \
295: OurExitString(stderr, foo, 1); \
296: } \
297: } \
298: terminalCursorAddress = ScreenInc(p); \
299: } \
300: /* if (pointer%LINESIZE == LINESIZE-1) { \
301: DoARefresh(); \
302: if (TtyChars() > MAX_CHANGES_BEFORE_POLL) { \
303: EmptyTerminal(); \
304: } \
305: } */ \
306: }
307:
308: /* run through screen, printing out non-null lines */
309:
310: /* There are two separate reasons for wanting to terminate this
311: * loop early. One is to respond to new input (either from
312: * the terminal or from the network [host]). For this reason,
313: * we expect to see 'HaveInput' come true when new input comes in.
314: *
315: * The second reason is a bit more difficult (for me) to understand.
316: * Basically, we don't want to get too far ahead of the characters that
317: * appear on the screen. Ideally, we would type out a few characters,
318: * wait until they appeared on the screen, then type out a few more.
319: * The reason for this is that the user, on seeing some characters
320: * appear on the screen may then start to type something. We would
321: * like to look at what the user types at about the same 'time'
322: * (measured by characters being sent to the terminal) that the
323: * user types them. For this reason, what we would like to do
324: * is update a bit, then call curses to do a refresh, flush the
325: * output to the terminal, then wait until the terminal data
326: * has been sent.
327: *
328: * Note that curses is useful for, among other things, deciding whether
329: * or not to send :ce: (clear to end of line), so we should call curses
330: * at end of lines (beginning of next lines).
331: *
332: * The problems here are the following: If we do lots of write(2)s,
333: * we will be doing lots of context switches, thus lots of overhead
334: * (which we have already). Second, if we do a select to wait for
335: * the output to drain, we have to contend with the fact that NOW
336: * we are scheduled to run, but who knows what the scheduler will
337: * decide when the output has caught up.
338: */
339:
340: if (Highest == HighestScreen()) {
341: Highest = ScreenDec(Highest); /* else, while loop will never end */
342: }
343: if (Lowest < LowestScreen()) {
344: Lowest = LowestScreen(); /* could be -1 in some cases with
345: * unformatted screens.
346: */
347: }
348: if (Highest >= Lowest) {
349: /* if there is anything to do, do it. We won't terminate
350: * the loop until we've gone at least to Highest.
351: */
352: pointer = Lowest;
353: while ((pointer <= Highest) && !HaveInput) {
354:
355: /* point at the next place of disagreement */
356: pointer += (bunequal(Host+pointer, Terminal+pointer,
357: (Highest-pointer+1)*sizeof Host[0])/sizeof Host[0]);
358:
359: /* how many characters to change until the end of the
360: * current line
361: */
362: changes = LINESIZE - ScreenLineOffset(pointer);
363:
364: /* what is the field attribute of the current position */
365: fieldattr = FieldAttributes(WhereAttrByte(pointer));
366:
367: if ((IsStartField(pointer) != TermIsStartField(pointer)) ||
368: (IsStartField(pointer) &&
369: fieldattr != TermAttributes(pointer))) {
370:
371: int oldterm;
372:
373: oldterm = TermAttributes(pointer);
374: if (IsStartField(pointer)) {
375: TermNewField(pointer, fieldattr);
376: SetTerminal(pointer, 0);
377: } else {
378: TermDeleteField(pointer);
379: }
380: /* We always do the first character in a divergent
381: * field, since otherwise the start of a field in
382: * the Host structure may leave a highlighted blank
383: * on the screen, and the start of a field in the
384: * Terminal structure may leave a non-highlighted
385: * something in the middle of a highlighted field
386: * on the screen.
387: */
388: SetHighlightMode(pointer);
389: c = GetHost(pointer);
390: DoCharacterAt(c,pointer);
391:
392: if (NotVisuallyCompatibleAttributes
393: (pointer, fieldattr, oldterm)) {
394: int j;
395:
396: j = pointer;
397:
398: pointer = ScreenInc(pointer);
399: SetHighlightMode(pointer); /* Turn on highlighting */
400: while (!IsStartField(pointer) &&
401: !TermIsStartField(pointer)) {
402: c = GetHost(pointer);
403: DoCharacterAt(c,pointer); /* MACRO */
404: pointer = ScreenInc(pointer);
405: if (!(--changes)) {
406: DoARefresh();
407: EmptyTerminal();
408: /* We don't look at HaveInput here, since
409: * if we leave this loop before the end of
410: * the 3270 field, we could have pointer
411: * higher than Highest. This would cause
412: * us to end the highest "while" loop,
413: * but we may, in fact, need to go around the
414: * screen once again.
415: */
416: }
417: /* The loop needs to be protected
418: * from the situation where there had been only
419: * one field on the Terminal, and none on the Host.
420: * In this case, we have just deleted our last
421: * field. Hence, the break.
422: */
423: if (j == pointer) {
424: break;
425: }
426: }
427: if (!TermIsStartField(pointer)) {
428: /* Remember what the terminal looked like */
429: TermNewField(pointer, oldterm);
430: SetTerminal(pointer, 0);
431: /* The danger here is that the current position may
432: * be the start of a Host field. If so, and the field
433: * is highlighted, and our terminal was highlighted,
434: * then we will leave a highlighted blank at this
435: * position.
436: */
437: SetHighlightMode(pointer);
438: c = 0;
439: DoCharacterAt(c,pointer);
440: }
441: /* We could be in the situation of needing to exit.
442: * This could happen if the current field wrapped around
443: * the end of the screen.
444: */
445: if (j > pointer) {
446: break;
447: }
448: } else {
449: c = GetHost(pointer);
450: /* We always do the first character in a divergent
451: * field, since otherwise the start of a field in
452: * the Host structure may leave a highlighted blank
453: * on the screen, and the start of a field in the
454: * Terminal structure may leave a non-highlighted
455: * something in the middle of a highlighted field
456: * on the screen.
457: */
458: SetHighlightMode(pointer);
459: DoCharacterAt(c,pointer);
460: }
461: } else {
462: SetHighlightMode(pointer);
463: /* The following will terminate at least when we get back
464: * to the original 'pointer' location (since we force
465: * things to be equal).
466: */
467: while (((c = GetHost(pointer)) != GetTerminal(pointer)) &&
468: !IsStartField(pointer) && !TermIsStartField(pointer)) {
469: DoCharacterAt(c, pointer);
470: pointer = ScreenInc(pointer);
471: if (!(--changes)) {
472: DoARefresh();
473: EmptyTerminal();
474: if (HaveInput) { /* if input came in, take it */
475: break;
476: }
477: }
478: }
479: }
480: }
481: }
482: DoARefresh();
483: Lowest = pointer;
484: if (Lowest > Highest) { /* if we finished input... */
485: Lowest = HighestScreen()+1;
486: Highest = LowestScreen()-1;
487: terminalCursorAddress = CorrectTerminalCursor();
488: if (ERR == move(ScreenLine(terminalCursorAddress),
489: ScreenLineOffset(terminalCursorAddress))) {
490: OurExitString(stderr, "ERR from move\n", 1);
491: }
492: DoARefresh();
493: if (needToRing) {
494: StringToTerminal(bellSequence);
495: needToRing = 0;
496: }
497: }
498: EmptyTerminal(); /* move data along */
499: return;
500: }
501:
502:
503: /* returns a 1 if no more output available (so, go ahead and block),
504: or a 0 if there is more output available (so, just poll the other
505: sources/destinations, don't block).
506: */
507:
508: int
509: DoTerminalOutput()
510: {
511: /* called just before a select to conserve IO to terminal */
512: if (Initialized &&
513: ((Lowest <= Highest) || needToRing ||
514: (terminalCursorAddress != CorrectTerminalCursor()))) {
515: TryToSend();
516: }
517: if (Lowest > Highest) {
518: return(1); /* no more output now */
519: } else {
520: return(0); /* more output for future */
521: }
522: }
523:
524: /* returns the number of characters consumed */
525: int
526: DataFromNetwork(buffer, count, control)
527: register char *buffer; /* what the data is */
528: register int count; /* and how much there is */
529: int control; /* this buffer terminated block */
530: {
531: int origCount;
532: register int c;
533: register int i;
534: static int Command;
535: static int Wcc;
536: static int LastWasTerminated = 0; /* was "control" = 1 last time? */
537:
538: if (!Initialized) { /* not initialized */
539: int abort();
540:
541: bzero((char *)Host, sizeof Host);
542: DeleteAllFields();
543: for (i = 0; i < sizeof Blanks; i++) {
544: Blanks[i] = ' ';
545: }
546: bzero((char *)Terminal, sizeof Terminal);
547: Lowest = HighestScreen()+1;
548: Highest = LowestScreen()-1;
549: terminalCursorAddress =
550: CursorAddress =
551: BufferAddress = SetBufferAddress(0,0);
552: UnLocked = 1;
553: StartScreen();
554: LastWasTerminated = 1;
555: Initialized = 1;
556: OutputClock = 1;
557: TransparentClock = -1;
558: signal(SIGHUP, abort);
559: }
560:
561: origCount = count;
562:
563: if (LastWasTerminated) {
564:
565: if (count < 2) {
566: if (count == 0) {
567: StringToTerminal("Short count received from host!\n");
568: return(count);
569: }
570: Command = buffer[0]&0xff;
571: switch (Command) { /* This had better be a read command */
572: case CMD_READ_MODIFIED:
573: DoReadModified();
574: break;
575: case CMD_READ_BUFFER:
576: DoReadBuffer();
577: break;
578: default:
579: break;
580: }
581: return(1); /* We consumed everything */
582: }
583: Command = buffer[0]&0xff;
584: Wcc = buffer[1]&0xff;
585: if (Wcc & WCC_RESET_MDT) {
586: i = c = WhereAttrByte(LowestScreen());
587: do {
588: if (HasMdt(i)) {
589: TurnOffMdt(i);
590: }
591: i = FieldInc(i);
592: } while (i != c);
593: }
594:
595: switch (Command) {
596: case CMD_ERASE_WRITE:
597: Clear3270();
598: if (TransparentClock == OutputClock) {
599: clearok(curscr, TRUE);
600: }
601: break;
602: case CMD_ERASE_ALL_UNPROTECTED:
603: CursorAddress = HighestScreen()+1;
604: for (i = LowestScreen(); i <= HighestScreen(); ScreenInc(i)) {
605: if (IsUnProtected(i)) {
606: if (CursorAddress > i) {
607: CursorAddress = i;
608: }
609: AddHost(i, '\0');
610: }
611: if (HasMdt(i)) {
612: TurnOffMdt(i);
613: }
614: }
615: if (CursorAddress == HighestScreen()+1) {
616: CursorAddress = SetBufferAddress(0,0);
617: }
618: UnLocked = 1;
619: AidByte = 0;
620: break;
621: case CMD_WRITE:
622: break;
623: default:
624: break;
625: }
626:
627: count -= 2; /* strip off command and wcc */
628: buffer += 2;
629:
630: }
631: LastWasTerminated = 0; /* then, reset at end... */
632:
633: while (count) {
634: count--;
635: c = (*buffer++)&0xff;
636: if (IsOrder(c)) {
637: /* handle an order */
638: switch (c) {
639: # define Ensure(x) if (count < x) { \
640: if (!control) { \
641: return(origCount-(count+1)); \
642: } else { \
643: /* XXX - should not occur */ \
644: count = 0; \
645: break; \
646: } \
647: }
648: case ORDER_SF:
649: Ensure(1);
650: c = (*buffer++)&0xff;
651: count--;
652: if ( ! (IsStartField(BufferAddress) &&
653: FieldAttributes(BufferAddress) == c)) {
654: if (NotVisuallyCompatibleAttributes(BufferAddress, c,
655: FieldAttributes(BufferAddress))) {
656: SetHighestLowest(BufferAddress);
657: }
658: if (GetTerminal(BufferAddress)) {
659: SetHighestLowest(BufferAddress);
660: }
661: NewField(BufferAddress,c);
662: }
663: SetHost(BufferAddress, 0);
664: BufferAddress = ScreenInc(BufferAddress);
665: break;
666: case ORDER_SBA:
667: Ensure(2);
668: i = buffer[0];
669: c = buffer[1];
670: if (!i && !c) { /* transparent write */
671: if (!control) {
672: return(origCount-(count+1));
673: } else {
674: while (Initialized &&
675: ((Lowest <= Highest) || needToRing ||
676: (terminalCursorAddress !=
677: CorrectTerminalCursor()))) {
678: extern int HaveInput;
679:
680: HaveInput = 0;
681: TryToSend();
682: }
683: if (TransparentClock != OutputClock) {
684: if (!DoTerminalOutput()) {
685: return(origCount-(count+1));
686: }
687: move(ScreenLine(CursorAddress),
688: ScreenLineOffset(CursorAddress));
689: DoARefresh();
690: }
691: TransparentClock = OutputClock; /* this clock */
692: (void) DataToTerminal(buffer+2, count-2);
693: SendToIBM();
694: TransparentClock = OutputClock+1; /* clock next */
695: buffer += count;
696: count -= count;
697: }
698: } else {
699: BufferAddress = Addr3270(i, c);
700: buffer += 2;
701: count -= 2;
702: }
703: break;
704: case ORDER_IC:
705: CursorAddress = BufferAddress;
706: break;
707: case ORDER_PT:
708: for (i = ScreenInc(BufferAddress); (i != HighestScreen());
709: i = ScreenInc(i)) {
710: if (IsStartField(i)) {
711: i = ScreenInc(i);
712: if (!IsProtected(ScreenInc(i))) {
713: break;
714: }
715: if (i == HighestScreen()) {
716: break;
717: }
718: }
719: }
720: CursorAddress = i;
721: break;
722: case ORDER_RA:
723: Ensure(2);
724: i = Addr3270(buffer[0], buffer[1]);
725: c = buffer[2];
726: do {
727: AddHost(BufferAddress, c);
728: BufferAddress = ScreenInc(BufferAddress);
729: } while (BufferAddress != i);
730: buffer += 3;
731: count -= 3;
732: break;
733: case ORDER_EUA: /* (from [here,there), ie: half open interval] */
734: Ensure(2);
735: c = FieldAttributes(WhereAttrByte(BufferAddress));
736: for (i = Addr3270(buffer[0], buffer[1]); i != BufferAddress;
737: BufferAddress = ScreenInc(BufferAddress)) {
738: if (!IsProtectedAttr(BufferAddress, c)) {
739: AddHost(BufferAddress, 0);
740: }
741: }
742: buffer += 2;
743: count -= 2;
744: break;
745: case ORDER_YALE: /* special YALE defined order */
746: Ensure(2); /* need at least two characters */
747: if ((*buffer&0xff) == 0x5b) {
748: i = OptOrder(buffer+1, count-1, control);
749: if (i == 0) {
750: return(origCount-(count+1)); /* come here again */
751: } else {
752: buffer += 1 + i;
753: count - = (1 + i);
754: }
755: }
756: break;
757: default:
758: break; /* XXX ? */
759: }
760: if (count < 0) {
761: count = 0;
762: }
763: } else {
764: /* Data comes in large clumps - take it all */
765: i = BufferAddress;
766: #ifdef NOTDEF
767: AddHost(i, c);
768: #else /* NOTDEF */
769: AddHostA(i, c);
770: SetHighestLowest(i);
771: #endif /* NOTDEF */
772: i = ScreenInc(i);
773: while (count && !IsOrder(c = *(buffer)&0xff)) {
774: buffer++;
775: #ifdef NOTDEF
776: AddHost(i, c);
777: #else /* NOTDEF */
778: AddHostA(i, c);
779: #endif /* NOTDEF */
780: i = ScreenInc(i);
781: #ifndef NOTDEF
782: if (i == LowestScreen()) {
783: SetHighestLowest(HighestScreen());
784: }
785: #endif /* NOTDEF */
786: count--;
787: }
788: #ifndef NOTDEF
789: SetHighestLowest(i);
790: #endif /* NOTDEF */
791: BufferAddress = i;
792: }
793: }
794: if (count == 0) {
795: OutputClock++; /* time rolls on */
796: if (control) {
797: if (Wcc & WCC_RESTORE) {
798: if (TransparentClock != OutputClock) {
799: AidByte = 0;
800: }
801: UnLocked = 1;
802: (void) TerminalIn(); /* move along user's input */
803: }
804: if (Wcc & WCC_ALARM) {
805: RingBell();
806: }
807: }
808: LastWasTerminated = control; /* state for next time */
809: return(origCount);
810: } else {
811: return(origCount-count);
812: }
813: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.