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: 24: /* test stub for DataFrom3270, etc. */ 25: 26: #define DEFINEAIDS 27: #include "m4.out" /* output of termcodes.m4 */ 28: #include "ascebc.h" 29: #include "3270.h" 30: #include "screen.h" 31: #include "options.h" 32: #include "ectype.h" 33: 34: #ifndef lint 35: static char sccsid[] = "@(#)keyboard.c 2.6 4/4/86"; 36: #endif /* ndef lint */ 37: 38: #define EmptyChar (ourPTail == ourBuffer) 39: #define FullChar (ourPTail == ourBuffer+sizeof ourBuffer) 40: 41: extern char ascebc[NASCEBC][NASCII]; 42: 43: static char ourBuffer[4000]; 44: 45: static char *ourPHead = ourBuffer, 46: *ourPTail = ourBuffer; 47: 48: static int trTbl = AE_IN; /* which ascii->ebcdic tr table */ 49: 50: static int HadAid = 0; /* Had an AID haven't sent */ 51: 52: /* the following are global variables */ 53: 54: extern int UnLocked; /* keyboard is UnLocked? */ 55: 56: /* Tab() - sets cursor to the start of the next unprotected field */ 57: static void 58: Tab() 59: { 60: register int i, j; 61: 62: i = CursorAddress; 63: j = WhereAttrByte(CursorAddress); 64: do { 65: if (IsStartField(i) && IsUnProtected(ScreenInc(i))) { 66: break; 67: } 68: i = FieldInc(i); 69: } while (i != j); 70: if (IsStartField(i) && IsUnProtected(ScreenInc(i))) { 71: CursorAddress = ScreenInc(i); 72: } else { 73: CursorAddress = SetBufferAddress(0,0); 74: } 75: } 76: 77: 78: /* BackTab() - sets cursor to the start of the most recent field */ 79: 80: static void 81: BackTab() 82: { 83: register int i; 84: 85: i = ScreenDec(CursorAddress); 86: for (;;) { 87: if (IsStartField(ScreenDec(i)) && IsUnProtected(i)) { 88: CursorAddress = i; 89: break; 90: } 91: if (i == CursorAddress) { 92: CursorAddress = SetBufferAddress(0,0); 93: break; 94: } 95: i = ScreenDec(i); 96: } 97: } 98: 99: 100: /* EraseEndOfField - erase all characters to the end of a field */ 101: 102: static 103: EraseEndOfField() 104: { 105: register int i; 106: 107: if (IsProtected(CursorAddress)) { 108: RingBell(); 109: } else { 110: TurnOnMdt(CursorAddress); 111: i = CursorAddress; 112: do { 113: AddHost(i, 0); 114: i = ScreenInc(i); 115: } while ((i != CursorAddress) && IsUnProtected(i)); 116: } 117: } 118: 119: /* Delete() - deletes a character from the screen 120: * 121: * What we want to do is delete the section 122: * [where, from-1] from the screen, 123: * filling in with what comes at from. 124: * 125: * The deleting continues to the end of the field (or 126: * until the cursor wraps). 127: * 128: * From can be a start of a field. We 129: * check for that. However, there can't be any 130: * fields that start between where and from. 131: * We don't check for that. 132: * 133: * Also, we assume that the protection status of 134: * everything has been checked by the caller. 135: * 136: */ 137: 138: static 139: Delete(where, from) 140: register int where, /* Where to start deleting from */ 141: from; /* Where to pull back from */ 142: { 143: register int i; 144: 145: TurnOnMdt(where); /* Only do this once in this field */ 146: i = where; 147: do { 148: if (IsStartField(from)) { 149: AddHost(i, 0); /* Stick the edge at the start field */ 150: } else { 151: AddHost(i, GetHost(from)); 152: from = ScreenInc(from); /* Move the edge */ 153: } 154: i = ScreenInc(i); 155: } while ((!IsStartField(i)) && (i != where)); 156: } 157: 158: ColBak() 159: { 160: register int i; 161: 162: i = ScreenLineOffset(CursorAddress); 163: for (i = i-1; i >= 0; i--) { 164: if (OptColTabs[i]) { 165: break; 166: } 167: } 168: if (i < 0) { 169: i = 0; 170: } 171: CursorAddress = SetBufferAddress(ScreenLine(CursorAddress), i); 172: } 173: 174: ColTab() 175: { 176: register int i; 177: 178: i = ScreenLineOffset(CursorAddress); 179: for (i = i+1; i < LINESIZE; i++) { 180: if (OptColTabs[i]) { 181: break; 182: } 183: } 184: if (i >= LINESIZE) { 185: i = LINESIZE-1; 186: } 187: CursorAddress = SetBufferAddress(ScreenLine(CursorAddress), i); 188: } 189: 190: static 191: Home() 192: { 193: register int i; 194: register int j; 195: 196: i = SetBufferAddress(OptHome, 0); 197: j = WhereLowByte(i); 198: do { 199: if (IsUnProtected(i)) { 200: CursorAddress = i; 201: return; 202: } 203: /* the following could be a problem if we got here with an 204: * unformatted screen. However, this is "impossible", since 205: * with an unformatted screen, the IsUnProtected(i) above 206: * should be true. 207: */ 208: i = ScreenInc(FieldInc(i)); 209: } while (i != j); 210: CursorAddress = LowestScreen(); 211: } 212: 213: static 214: LastOfField(i) 215: register int i; /* position to start from */ 216: { 217: register int j; 218: register int k; 219: 220: k = j = i; 221: while (IsProtected(i) || Eisspace(GetHost(i))) { 222: i = ScreenInc(i); 223: if (i == j) { 224: break; 225: } 226: } 227: /* We are now IN a word IN an unprotected field (or wrapped) */ 228: while (!IsProtected(i)) { 229: if (!Eisspace(GetHost(i))) { 230: k = i; 231: } 232: i = ScreenInc(i); 233: if (i == j) { 234: break; 235: } 236: } 237: return(k); 238: } 239: 240: 241: static 242: FlushChar() 243: { 244: ourPTail = ourPHead = ourBuffer; 245: } 246: 247: 248: static 249: AddChar(character) 250: char character; 251: { 252: *ourPHead++ = character; 253: } 254: 255: 256: static void 257: SendUnformatted() 258: { 259: register int i, j; 260: register int Nulls; 261: register int c; 262: 263: /* look for start of field */ 264: Nulls = 0; 265: i = j = LowestScreen(); 266: do { 267: c = GetHost(i); 268: if (c == 0) { 269: Nulls++; 270: } else { 271: while (Nulls) { 272: Nulls--; 273: AddChar(0x40); /* put in blanks */ 274: } 275: AddChar(c); 276: } 277: i = ScreenInc(i); 278: } while (i != j); 279: } 280: 281: static 282: SendField(i) 283: register int i; /* where we saw MDT bit */ 284: { 285: register int j; 286: register int k; 287: register int Nulls; 288: register int c; 289: 290: /* look for start of field */ 291: i = j = WhereLowByte(i); 292: 293: AddChar(ORDER_SBA); /* set start field */ 294: AddChar(BufferTo3270_0(j)); /* set address of this field */ 295: AddChar(BufferTo3270_1(j)); 296: 297: if (!IsStartField(j)) { 298: Nulls = 0; 299: k = ScreenInc(WhereHighByte(j)); 300: do { 301: c = GetHost(j); 302: if (c == 0) { 303: Nulls++; 304: } else { 305: while (Nulls) { 306: Nulls--; 307: AddChar(0x40); /* put in blanks */ 308: } 309: AddChar(c); 310: } 311: j = ScreenInc(j); 312: } while ((j != k) && (j != i)); 313: } 314: return(j); 315: } 316: 317: /* Various types of reads... */ 318: DoReadModified() 319: { 320: register int i, j; 321: 322: if (AidByte) { 323: AddChar(AidByte); 324: } else { 325: AddChar(0x60); 326: } 327: if ((AidByte != AID_PA1) && (AidByte != AID_PA2) && (AidByte != AID_PA3) 328: && (AidByte != AID_CLEAR)) { 329: AddChar(BufferTo3270_0(CursorAddress)); 330: AddChar(BufferTo3270_1(CursorAddress)); 331: i = j = WhereAttrByte(LowestScreen()); 332: /* Is this an unformatted screen? */ 333: if (!IsStartField(i)) { /* yes, handle separate */ 334: SendUnformatted(); 335: } else { 336: do { 337: if (HasMdt(i)) { 338: i = SendField(i); 339: } else { 340: i = FieldInc(i); 341: } 342: } while (i != j); 343: } 344: } 345: ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail); 346: if (ourPTail == ourPHead) { 347: FlushChar(); 348: HadAid = 0; /* killed that buffer */ 349: } 350: } 351: 352: /* A read buffer operation... */ 353: 354: DoReadBuffer() 355: { 356: register int i, j; 357: 358: if (AidByte) { 359: AddChar(AidByte); 360: } else { 361: AddChar(0x60); 362: } 363: AddChar(BufferTo3270_0(CursorAddress)); 364: AddChar(BufferTo3270_1(CursorAddress)); 365: i = j = LowestScreen(); 366: do { 367: if (IsStartField(i)) { 368: AddChar(ORDER_SF); 369: AddChar(BufferTo3270_1(FieldAttributes(i))); 370: } else { 371: AddChar(GetHost(i)); 372: } 373: i = ScreenInc(i); 374: } while (i != j); 375: ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail); 376: if (ourPTail == ourPHead) { 377: FlushChar(); 378: HadAid = 0; /* killed that buffer */ 379: } 380: } 381: /* Try to send some data to host */ 382: 383: SendToIBM() 384: { 385: extern int TransparentClock, OutputClock; 386: 387: if (TransparentClock == OutputClock) { 388: if (HadAid) { 389: AddChar(AidByte); 390: HadAid = 0; 391: } else { 392: AddChar(0xe8); 393: } 394: do { 395: ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail); 396: } while (ourPTail != ourPHead); 397: FlushChar(); 398: } else if (HadAid) { 399: DoReadModified(); 400: } 401: netflush(); 402: } 403: 404: /* This takes in one character from the keyboard and places it on the 405: * screen. 406: */ 407: 408: static 409: OneCharacter(c, insert) 410: int c; /* character (Ebcdic) to be shoved in */ 411: int insert; /* are we in insert mode? */ 412: { 413: register int i, j; 414: 415: if (IsProtected(CursorAddress)) { 416: RingBell(); 417: return; 418: } 419: if (insert) { 420: /* is the last character in the field a blank or null? */ 421: i = ScreenDec(FieldInc(CursorAddress)); 422: j = GetHost(i); 423: if (!Eisspace(j)) { 424: RingBell(); 425: return; 426: } else { 427: for (j = ScreenDec(i); i != CursorAddress; 428: j = ScreenDec(j), i = ScreenDec(i)) { 429: AddHost(i, GetHost(j)); 430: } 431: } 432: } 433: AddHost(CursorAddress, c); 434: TurnOnMdt(CursorAddress); 435: CursorAddress = ScreenInc(CursorAddress); 436: if (IsStartField(CursorAddress) && 437: ((FieldAttributes(CursorAddress)&ATTR_AUTO_SKIP_MASK) == 438: ATTR_AUTO_SKIP_VALUE)) { 439: Tab(); 440: } 441: } 442: 443: /* go through data until an AID character is hit, then generate an interrupt */ 444: 445: DataFrom3270(buffer, count) 446: char *buffer; /* where the data is */ 447: int count; /* how much data there is */ 448: { 449: int origCount; 450: register int c; 451: register int i; 452: register int j; 453: static int InsertMode = 0; /* is the terminal in insert mode? */ 454: 455: extern int OutputClock, TransparentClock; 456: 457: if (!UnLocked || HadAid) { 458: if (HadAid) { 459: SendToIBM(); 460: if (!EmptyChar) { 461: return(0); /* nothing to do */ 462: } 463: } 464: if (!HadAid && (((*buffer&0xff) == TC_RESET) || 465: ((*buffer&0xff) == TC_MASTER_RESET)) && EmptyChar) { 466: UnLocked = 1; 467: } 468: if (!UnLocked) { 469: return(0); 470: } 471: } 472: /* now, either empty, or haven't seen aid yet */ 473: 474: origCount = count; 475: 476: if (TransparentClock == OutputClock) { 477: while (count) { 478: c = (*buffer++)&0xff; 479: count--; 480: if (IsAid(c)) { 481: UnLocked = 0; 482: InsertMode = 0; 483: AidByte = TCtoAid(c); 484: HadAid = 1; 485: } else { 486: switch (c) { 487: case TC_ESCAPE: 488: Stop3270(1); 489: command(0); 490: ConnectScreen(); 491: break; 492: 493: case TC_RESET: 494: case TC_MASTER_RESET: 495: UnLocked = 1; 496: break; 497: 498: default: 499: return(origCount-(count+1)); 500: } 501: } 502: } 503: } 504: 505: while (count) { 506: c = (*buffer++)&0xff; 507: count--; 508: 509: if (!IsTc(c)) { 510: /* Add the character to the buffer */ 511: OneCharacter(ascebc[trTbl][c], InsertMode); 512: } else if (IsAid(c)) { /* got Aid */ 513: if (c == TC_CLEAR) { 514: LocalClear3270(); 515: } 516: UnLocked = 0; 517: InsertMode = 0; /* just like a 3278 */ 518: AidByte = TCtoAid(c); 519: HadAid = 1; 520: SendToIBM(); 521: return(origCount-count); 522: } else { 523: 524: /* non-AID TC character */ 525: switch (c) { 526: 527: case TC_ERASE: 528: if (IsProtected(ScreenDec(CursorAddress))) { 529: RingBell(); 530: } else { 531: CursorAddress = ScreenDec(CursorAddress); 532: Delete(CursorAddress, ScreenInc(CursorAddress)); 533: } 534: break; 535: 536: case TC_WERASE: 537: j = CursorAddress; 538: i = ScreenDec(j); 539: if (IsProtected(i)) { 540: RingBell(); 541: } else { 542: while ((!IsProtected(i) && Eisspace(GetHost(i))) 543: && (i != j)) { 544: i = ScreenDec(i); 545: } 546: /* we are pointing at a character in a word, or 547: * at a protected position 548: */ 549: while ((!IsProtected(i) && !Eisspace(GetHost(i))) 550: && (i != j)) { 551: i = ScreenDec(i); 552: } 553: /* we are pointing at a space, or at a protected 554: * position 555: */ 556: CursorAddress = ScreenInc(i); 557: Delete(CursorAddress, j); 558: } 559: break; 560: 561: case TC_FERASE: 562: if (IsProtected(CursorAddress)) { 563: RingBell(); 564: } else { 565: CursorAddress = ScreenInc(CursorAddress); /* for btab */ 566: BackTab(); 567: EraseEndOfField(); 568: } 569: break; 570: 571: case TC_RESET: 572: InsertMode = 0; 573: break; 574: 575: case TC_MASTER_RESET: 576: InsertMode = 0; 577: RefreshScreen(); 578: break; 579: 580: case TC_UP: 581: CursorAddress = ScreenUp(CursorAddress); 582: break; 583: 584: case TC_LEFT: 585: CursorAddress = ScreenDec(CursorAddress); 586: break; 587: 588: case TC_RIGHT: 589: CursorAddress = ScreenInc(CursorAddress); 590: break; 591: 592: case TC_DOWN: 593: CursorAddress = ScreenDown(CursorAddress); 594: break; 595: 596: case TC_DELETE: 597: if (IsProtected(CursorAddress)) { 598: RingBell(); 599: } else { 600: Delete(CursorAddress, ScreenInc(CursorAddress)); 601: } 602: break; 603: 604: case TC_INSRT: 605: InsertMode = !InsertMode; 606: break; 607: 608: case TC_HOME: 609: Home(); 610: break; 611: 612: case TC_NL: 613: /* The algorithm is to look for the first unprotected 614: * column after column 0 of the following line. Having 615: * found that unprotected column, we check whether the 616: * cursor-address-at-entry is at or to the right of the 617: * LeftMargin AND the LeftMargin column of the found line 618: * is unprotected. If this conjunction is true, then 619: * we set the found pointer to the address of the LeftMargin 620: * column in the found line. 621: * Then, we set the cursor address to the found address. 622: */ 623: i = SetBufferAddress(ScreenLine(ScreenDown(CursorAddress)), 0); 624: j = ScreenInc(WhereAttrByte(CursorAddress)); 625: do { 626: if (IsUnProtected(i)) { 627: break; 628: } 629: /* Again (see comment in Home()), this COULD be a problem 630: * with an unformatted screen. 631: */ 632: /* If there was a field with only an attribute byte, 633: * we may be pointing to the attribute byte of the NEXT 634: * field, so just look at the next byte. 635: */ 636: if (IsStartField(i)) { 637: i = ScreenInc(i); 638: } else { 639: i = ScreenInc(FieldInc(i)); 640: } 641: } while (i != j); 642: if (!IsUnProtected(i)) { /* couldn't find unprotected */ 643: i = SetBufferAddress(0,0); 644: } 645: if (OptLeftMargin <= ScreenLineOffset(CursorAddress)) { 646: if (IsUnProtected(SetBufferAddress(ScreenLine(i), 647: OptLeftMargin))) { 648: i = SetBufferAddress(ScreenLine(i), OptLeftMargin); 649: } 650: } 651: CursorAddress = i; 652: break; 653: 654: case TC_EINP: 655: i = j = ScreenInc(WhereAttrByte(LowestScreen())); 656: do { 657: if (IsUnProtected(i)) { 658: AddHost(i, 0); 659: TurnOnMdt(i); 660: } else { 661: /* FieldInc() puts us at the start of the next 662: * field. 663: * 664: * We don't want to skip to the start of the 665: * next field if we are on the attribute byte, 666: * since we may be skipping over an otherwise 667: * unprotected field. 668: * 669: * Also, j points at the first byte of the first 670: * field on the screen, unprotected or not. If 671: * we never point there, we might loop here for 672: * ever. 673: */ 674: if (!IsStartField(i)) { 675: i = FieldInc(i); 676: } 677: } 678: i = ScreenInc(i); 679: } while (i != j); 680: Home(); /* get to home position */ 681: break; 682: 683: case TC_EEOF: 684: EraseEndOfField(); 685: break; 686: 687: case TC_FM: 688: if (IsProtected(CursorAddress)) { 689: RingBell(); 690: } else { 691: OneCharacter(EBCDIC_FM, InsertMode); /* Add field mark */ 692: } 693: break; 694: 695: case TC_DP: 696: if (IsProtected(CursorAddress)) { 697: RingBell(); 698: break; 699: } 700: OneCharacter(EBCDIC_DUP, InsertMode); /* Add dup character */ 701: Tab(); 702: break; 703: 704: case TC_TAB: 705: Tab(); 706: break; 707: 708: case TC_BTAB: 709: BackTab(); 710: break; 711: 712: #ifdef NOTUSED /* Actually, this is superseded by unix flow 713: * control. 714: */ 715: case TC_XOFF: 716: Flow = 0; /* stop output */ 717: break; 718: 719: case TC_XON: 720: if (!Flow) { 721: Flow = 1; /* turn it back on */ 722: DoTerminalOutput(); 723: } 724: break; 725: #endif /* NOTUSED */ 726: 727: case TC_ESCAPE: 728: /* FlushChar(); do we want to flush characters from before? */ 729: Stop3270(1); 730: command(0); 731: ConnectScreen(); 732: break; 733: 734: case TC_DISC: 735: Stop3270(1); 736: suspend(); 737: ConnectScreen(); 738: break; 739: 740: case TC_RESHOW: 741: RefreshScreen(); 742: break; 743: 744: case TC_SETTAB: 745: OptColTabs[ScreenLineOffset(CursorAddress)] = 1; 746: break; 747: 748: case TC_DELTAB: 749: OptColTabs[ScreenLineOffset(CursorAddress)] = 0; 750: break; 751: 752: case TC_CLRTAB: 753: for (i = 0; i < sizeof OptColTabs; i++) { 754: OptColTabs[i] = 0; 755: } 756: break; 757: 758: case TC_COLTAB: 759: ColTab(); 760: break; 761: 762: case TC_COLBAK: 763: ColBak(); 764: break; 765: 766: case TC_INDENT: 767: ColTab(); 768: OptLeftMargin = ScreenLineOffset(CursorAddress); 769: break; 770: 771: case TC_UNDENT: 772: ColBak(); 773: OptLeftMargin = ScreenLineOffset(CursorAddress); 774: break; 775: 776: case TC_SETMRG: 777: OptLeftMargin = ScreenLineOffset(CursorAddress); 778: break; 779: 780: case TC_SETHOM: 781: OptHome = ScreenLine(CursorAddress); 782: break; 783: 784: /* 785: * Point to first character of next unprotected word on 786: * screen. 787: */ 788: case TC_WORDTAB: 789: i = CursorAddress; 790: while (!IsProtected(i) && !Eisspace(GetHost(i))) { 791: i = ScreenInc(i); 792: if (i == CursorAddress) { 793: break; 794: } 795: } 796: /* i is either protected, a space (blank or null), 797: * or wrapped 798: */ 799: while (IsProtected(i) || Eisspace(GetHost(i))) { 800: i = ScreenInc(i); 801: if (i == CursorAddress) { 802: break; 803: } 804: } 805: CursorAddress = i; 806: break; 807: 808: case TC_WORDBACKTAB: 809: i = ScreenDec(CursorAddress); 810: while (IsProtected(i) || Eisspace(GetHost(i))) { 811: i = ScreenDec(i); 812: if (i == CursorAddress) { 813: break; 814: } 815: } 816: /* i is pointing to a character IN an unprotected word 817: * (or i wrapped) 818: */ 819: while (!Eisspace(GetHost(i))) { 820: i = ScreenDec(i); 821: if (i == CursorAddress) { 822: break; 823: } 824: } 825: CursorAddress = ScreenInc(i); 826: break; 827: 828: /* Point to last non-blank character of this/next 829: * unprotected word. 830: */ 831: case TC_WORDEND: 832: i = ScreenInc(CursorAddress); 833: while (IsProtected(i) || Eisspace(GetHost(i))) { 834: i = ScreenInc(i); 835: if (i == CursorAddress) { 836: break; 837: } 838: } 839: /* we are pointing at a character IN an 840: * unprotected word (or we wrapped) 841: */ 842: while (!Eisspace(GetHost(i))) { 843: i = ScreenInc(i); 844: if (i == CursorAddress) { 845: break; 846: } 847: } 848: CursorAddress = ScreenDec(i); 849: break; 850: 851: /* Get to last non-blank of this/next unprotected 852: * field. 853: */ 854: case TC_FIELDEND: 855: i = LastOfField(CursorAddress); 856: if (i != CursorAddress) { 857: CursorAddress = i; /* We moved; take this */ 858: } else { 859: j = FieldInc(CursorAddress); /* Move to next field */ 860: i = LastOfField(j); 861: if (i != j) { 862: CursorAddress = i; /* We moved; take this */ 863: } 864: /* else - nowhere else on screen to be; stay here */ 865: } 866: break; 867: 868: default: 869: RingBell(); /* We don't handle this yet */ 870: } 871: } 872: } 873: return(origCount-count); 874: }