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