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: }