All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ascert.open.term.i3270.Tn3270StreamParser Maven / Gradle / Ivy

Go to download

An open source emulator supporting 3270 and potentially later 5250 terminal types.

The newest version!
/*
 * Copyright (c) 2016, 2017 Ascert, LLC.
 * www.ascert.com
 *
 * Based on original code from FreeHost3270, copyright for derivations from original works remain:
 *  Copyright (C) 1998, 2001  Art Gillespie
 *  Copyright (2) 2005 the http://FreeHost3270.Sourceforge.net
 *
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * The Author can be contacted at [email protected] or
 * 185 Captain Whitney Road (Becket)
 * Chester, MA  01011
 */
package com.ascert.open.term.i3270;

import java.io.IOException;

import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.ascert.open.ohio.Ohio;

import com.ascert.open.term.core.RWTelnet;
import com.ascert.open.term.core.TermChar;
import com.ascert.open.term.core.TermField;
import com.ascert.open.term.core.TnStreamParser;

/**
 * This class is the mack-daddy of the 3270 Engine, it takes the data from the RWTelnet class (through the refresh method of the TnAction
 * interface.) and makes sense of it.
 *
 * 

* It also implements all of the commands and orders outlined in the "3270 Data Stream Programmer's Reference" (GA23-0059-07) in chapters 3 * & 4. * * @since 0.1 */ public class Tn3270StreamParser implements TnStreamParser { private final static Logger log = Logger.getLogger(Tn3270StreamParser.class.getName()); public static final short[] addrTable = { 0x40, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F }; public static final int[] ebc2asc = { 0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 213, 46, 60, 40, 43, 124, 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 33, 36, 42, 41, 59, 126, 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 203, 44, 37, 95, 62, 63, 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 94, 204, 205, 206, 207, 208, 209, 229, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 91, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 93, 230, 231, 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255 }; public static final int[] asc2ebc = { 0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15, 16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31, 64, 90, 127, 123, 91, 108, 80, 125, 77, 93, 92, 78, 107, 96, 75, 97, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122, 94, 76, 126, 110, 111, 124, 193, 194, 195, 196, 197, 198, 199, 200, 201, 209, 210, 211, 212, 213, 214, 215, 216, 217, 226, 227, 228, 229, 230, 231, 232, 233, 173, 224, 189, 154, 109, 121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146, 147, 148, 149, 150, 151, 152, 153, 162, 163, 164, 165, 166, 167, 168, 169, 192, 79, 208, 95, 7, 32, 33, 34, 35, 36, 21, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27, 48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59, 4, 20, 62, 225, 65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87, 88, 89, 98, 99, 100, 101, 102, 103, 104, 105, 112, 113, 114, 115, 116, 117, 118, 119, 120, 128, 138, 139, 140, 141, 142, 143, 144, 106, 155, 156, 157, 158, 159, 160, 170, 171, 172, 74, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 161, 190, 191, 202, 203, 204, 205, 206, 207, 218, 219, 220, 221, 222, 223, 234, 235, 236, 237, 238, 239, 250, 251, 252, 253, 254, 255 }; /** * ************************************************************* */ /* TN3270 Commands */ /* See "Data Stream Programmer's reference p. 3.3 */ /** * ************************************************************* */ /** * Write command p. 3.5.1 */ public final static short CMD_W = 0x01; public final static short CMD_W_EBCDIC = 0xF1; /** * Erase/Write command p. 3.5.2 */ public final static short CMD_EW = 0x05; public final static short CMD_EW_EBCDIC = 0xF5; /** * Erase/Write Alternate command p 3.5.3 */ public final static short CMD_EWA = 0x0D; public final static short CMD_EWA_EBCDIC = 0x7E; /** * Read Buffer Command p 3.6.1 */ public final static short CMD_RB = 0x02; public final static short CMD_RB_EBCDIC = 0xF2; /** * Read Modified Command p 3.6.2 */ public final static short CMD_RM = 0x06; public final static short CMD_RM_EBCDIC = 0xF6; /** * Read Modified All Command p 3.6.2.5 */ public final static short CMD_RMA = 0x0E; public final static short CMD_RMA_EBCDIC = 0x6E; /** * Erase all unprotected command p. 3.5.5 */ public final static short CMD_EAU = 0x0F; public final static short CMD_EAU_EBCDIC = 0x6F; /** * Write Structured Field command p. 3.5.4 (Not supported in ASCII) */ public final static short CMD_WSF = 0x11; public final static short CMD_WSF_EBCDIC = 0xF3; /** * No-op */ public final static short CMD_NOOP = 0x03; /** * ********************************************************** */ /* TN3270 ORDERS */ /* p 4.3 table 4-1 */ /** * ********************************************************** */ /** * Start Field Order p 4.3.1 */ public final static short ORDER_SF = 0x1D; /** * Start Field Extended p 4.3.2 */ public final static short ORDER_SFE = 0x29; /** * Set Buffer Address p 4.3.3 */ public final static short ORDER_SBA = 0x11; /** * Set Attribute p 4.3.4 */ public final static short ORDER_SA = 0x28; /** * Modify Field p 4.3.5 */ public final static short ORDER_MF = 0x2C; /** * Insert Cursor p 4.3.6 */ public final static short ORDER_IC = 0x13; /** * Program Tab p 4.3.7 */ public final static short ORDER_PT = 0x05; /** * Repeat to Address p 4.3.8 */ public final static short ORDER_RA = 0x3C; /** * Erase Unprotected to Address p 4.3.9 */ public final static short ORDER_EUA = 0x12; /** * Graphic Escape p 4.3.10 */ public final static short ORDER_GE = 0x08; /** * ***************************************************************** */ /* Extended Attributes (see table 4-6 p 4.4.5) */ /** * ***************************************************************** */ /** * 3270 Field Attributes p 4.4.6.2 */ public final static short XA_SF = 0xC0; /** * Field Validation p 4.4.6.3 */ public final static short XA_VALIDATION = 0xC1; /** * Field Outlining p 4.4.6.6 */ public final static short XA_OUTLINING = 0xC2; /** * Extended Highlighting p 4.4.6.3 */ public final static short XA_HIGHLIGHTING = 0x41; /** * Foreground Color p 4.4.6.4 */ public final static short XA_FGCOLOR = 0x42; /** * Character Set 4.4.6.5 */ public final static short XA_CHARSET = 0x43; /** * Background Color p 4.4.6.4 */ public final static short XA_BGCOLOR = 0x45; /** * Transparency p 4.4.6.7 */ public final static short XA_TRANSPARENCY = 0x46; public final static short SF_RPQ_LIST = 0x00; public final static short SF_READ_PART = 0x01; public final static short SF_RP_QUERY = 0x02; public final static short SF_RP_QLIST = 0x03; public final static short SF_RPQ_EQUIV = 0x40; public final static short SF_RPQ_ALL = 0x80; /** * Structured field QCODES */ public static final short QUERY_REPLY = 0x81; public static final short SUMMARY_QUERY_REPLY = 0x80; public static final short COLOR_QUERY_REPLY = 0x86; public static final short HIGHLIGHT_QUERY_REPLY = 0x87; public static final short IMP_PART_QUERY_REPLY = 0xA6; private Term3270 rw; private Term3270Char[] chars = new Term3270Char[0]; private RWTelnet tn; private Vector fields = new Vector(); private int counter; private short[] dataIn; private int dataInLen; private boolean lastWasCommand; private boolean newSACommand; private int bufferAddr; private short foreground = Term3270Char.FGCOLOR_DEFAULT; private short background = Term3270Char.BGCOLOR_DEFAULT; private short highlight = Term3270Char.HL_NORMAL; public Tn3270StreamParser(Term3270 rw) { this.rw = rw; } /** * This method takes an input buffer and executes the appropriate tn3270 commands and orders. */ @Override public synchronized void parse(short[] inBuf, int inBufLen) throws IOException { if (log.isLoggable(Level.FINEST)) { StringBuffer inBufStr = new StringBuffer("parsing buffer: "); for (int i = 0; i < inBufLen; i++) { // prepending the hex digit with 0x prefix for // convenience of putting dumped data into tests inBufStr.append("0x").append(Integer.toHexString(inBuf[i])) .append(", "); } log.finer(inBufStr.toString()); } else if (log.isLoggable(Level.FINE)) { log.fine("parsing buffer"); } bufferAddr = rw.getCursorPosition(); fields = rw.getFields(); chars = (Term3270Char[]) rw.getCharBuffer(); dataIn = inBuf; dataInLen = inBufLen; //is the first byte an EBCDIC cmd, if so convert it switch (dataIn[0]) { case CMD_W_EBCDIC: dataIn[0] = CMD_W; break; case CMD_EW_EBCDIC: dataIn[0] = CMD_EW; break; case CMD_EWA_EBCDIC: dataIn[0] = CMD_EWA; break; case CMD_EAU_EBCDIC: dataIn[0] = CMD_EAU; break; case CMD_WSF_EBCDIC: dataIn[0] = CMD_WSF; break; case CMD_RB_EBCDIC: dataIn[0] = CMD_RB; break; case CMD_RM_EBCDIC: dataIn[0] = CMD_RM; break; case CMD_RMA_EBCDIC: dataIn[0] = CMD_RMA; break; } //now let's send the commands off to their appropriate methods: switch (dataIn[0]) { case CMD_W: case CMD_EW: case CMD_EWA: case CMD_EAU: log.finer("write operation"); lastWasCommand = true; writeOperation(); buildFields(); rw.getClient().refresh(); break; case CMD_WSF: lastWasCommand = true; log.finer("WSF"); writeStructuredField(dataIn); break; case CMD_RB: lastWasCommand = true; //System.out.println("Read Buffer..."); readBuffer(); break; case CMD_RM: lastWasCommand = true; //System.out.println("Read Modified..."); readModified(); break; case CMD_RMA: lastWasCommand = true; //System.out.println("Read Modified All..."); readModifiedAll(); break; default: throw new IOException("Invalid 3270 Command"); } rw.resumeParentThread(); } /** * From 3270 Data Stream Programmer's Reference: *

3.5 Write Operation

* The process of sending a write type command and performing that command is called a write operation. Five write commands are * initiated by the application program and performed by the display: *
    *
  • Write(W)
  • *
  • Erase/Write(EW)
  • *
  • Erase/Write Alternate(EWA)
  • *
  • Erase All Unprotected(EAU)
  • *
  • Write Structured Field(WSF)
  • *
* * From 3.1 * The format of a write command is as follows: * * * * * * *
Byte 1Byte 2Byte 3 ... n
Write CommandWCC (Write Control Character)Orders and Data
* *

3.4 Write Control Character (WCC) Byte

* The following table explains the interpretation of the WCC byte * * * * * * * * * *
Table 3-2. Write Control Character (WCC) Bit Definitions for Displays
BitExplanation
0N/A
1WCC reset bit. When set to 1, it resets partition characteristics to their system-defined defaults. When set to 0, * the current characteristics remain unchanged (no reset operations are performed).
2, 3 & 4Printer Operations N/A
5Sound alarm bit. When set to 1, it sounds the audible alarm at the end of the operation if that device has an * audible alarm.
6Keyboard Restore Bit. When set to 1, this bit unlocks the keyboard. it also resets the AID byte.
7Bit 7 resets MDT bits in the field attributes. When set to 1, all MDT bits in the device's existing character buffer are reset * before any data is written or orders are performed.
* */ private synchronized void writeOperation() { if (dataIn[0] == CMD_EAU) { log.fine("erase all unprotected"); eraseAllUnprotected(); return; } //now let's check the WCC for bit 0 if ((dataIn[1] & 0x01) != 0) { //Bit 7 is set to 1, reset all modified bits log.fine("reset MDT"); rw.resetMDT(); lastWasCommand = true; } switch (dataIn[0]) { case CMD_EW: case CMD_EWA: //System.err.println("Erase Write..."); lastWasCommand = true; eraseWrite(); break; case CMD_W: //System.err.println("Write..."); lastWasCommand = true; write(); break; } //check the post-operation functions in the WCC if ((dataIn[1] & 0x04) != 0) { //Bit 5 is set to 1 beep(); } if ((dataIn[1] & 0x02) != 0) { //Bit 2 is set to 1 rw.setKeyboardLocked(false); } } /** *

3.5.1 Write Command

* The Write command writes data into specified locations of the character buffer of partition 0 without erasing or modifying data in * the other locations. Data is stored in sucessive buffer locations until an order is encountered in the data stream that alters the * buffer address, or until all the data has been stored. During the write operation, the buffer address is advanced one location as * each character is stored. */ private synchronized void write() { lastWasCommand = true; newSACommand = false; //System.out.println(dataInLen); for (counter = 2; counter < dataInLen; counter++) { switch (dataIn[counter]) { case ORDER_SF: //System.err.println("SF: " + bufferAddr + " "); startField(); lastWasCommand = true; break; case ORDER_SFE: //System.err.println("SFE " + bufferAddr + " "); startFieldExtended(); lastWasCommand = true; break; case ORDER_SBA: //System.err.println("SBA " + bufferAddr + " "); bufferAddr = setBufferAddress(); //System.err.println("to: " + bufferAddr); lastWasCommand = true; break; case ORDER_SA: //System.err.println("SA " + bufferAddr + " "); setAttribute(); lastWasCommand = true; newSACommand = true; break; case ORDER_MF: //System.err.println("MF " + bufferAddr + " "); modifyField(); lastWasCommand = true; break; case ORDER_IC: //System.err.println("IC " + bufferAddr + " "); insertCursor(); lastWasCommand = true; break; case ORDER_PT: //System.err.println("PT " + lastWasCommand + " " + bufferAddr); programTab(); //System.out.println(" " + bufferAddr); break; case ORDER_RA: //System.err.print("RA " + bufferAddr + " "); repeatToAddress(); lastWasCommand = true; break; case ORDER_EUA: //System.err.print("EUA " + bufferAddr + " "); eraseUnprotectedToAddress(); lastWasCommand = true; break; case ORDER_GE: //System.err.print("GE " + " "); graphicEscape(); lastWasCommand = true; break; default: Term3270Char currChar = chars[bufferAddr++]; currChar.clear(); currChar.setChar((char) ebc2asc[dataIn[counter]]); //System.out.print(currChar.getChar()); if (newSACommand) { currChar.setForeground(foreground); currChar.setBackground(background); currChar.setHighlighting(highlight); } lastWasCommand = false; newSACommand = false; if (bufferAddr == chars.length) { bufferAddr = 0; } } } } /** *

3.5.2 Erase/Write Command

* The EW command does the following: *
    *
  • Sets the implicit partition size to the default size, if in implicit state
  • *
  • Resets a Program Check Indication, if one exists.
  • *
  • Erases the character buffer by writing null characters into all buffer locations.
  • *
  • Sets all the associated character attributes and extended field attributes to their default value(X'00).
  • *
  • Erases all field validation attributes.
  • *
  • Sets the current cursor position to 0. If directed to a partition, autoscroll is performed, if necessary, to position the window * at offset (0, 0).
  • *
  • If bit 1 of the WCC is set to 1, EW does the following:
  • *
      *
    • Resets the inbound reply mode to Field.
    • *
    • Resets to implicit partition state, if currently in explicit partitioned state. It destroys all partitions, creates implicit * partition 0 with default screen size, and sets inboud PID to 0 and INOP to Read Modified.
    • *
    *
  • Provides an acknoledgment of any outstanding read or enter if the keyboard restore bit in the WCC is set to 1.
  • *
  • Performs a write operation
  • *
*/ private synchronized void eraseWrite() { //set all buffer positions to 'null' for (int i = 0; i < chars.length; i++) { chars[i].clear(); } rw.setCursorPosition((short) 0); bufferAddr = 0; write(); rw.setFieldsChanged(true); } /** *

3.5.5 Erase All Unprotected Command

* EAU does the following: *
    *
  • Clears all the unprotected character locations of the partition to nulls and sets any character attributes affected to their * default values.
  • *
  • Resets to 0 the MDT bit in the field attribute for each unprotected field
  • *
  • Unlocks the keyboard
  • *
  • Resets the AID
  • *
  • Repositions the cursor to the first character location, after the field attribute, in the first unprotected field of the * partition's character buffer.
  • *
*/ private synchronized void eraseAllUnprotected() { for (int i = 0; i < chars.length; i++) { Term3270Char c = chars[i]; TermField f = c.getField(); if ((f != null) && !f.isProtected()) { c.setChar((char) 0); try { f.setModified(false); } catch (Exception e) { log.warning(e.getMessage()); } } else if (f == null) { //not in a field -- unprotected by default c.setChar((char) 0); } //unlock the keyboard rw.setKeyboardLocked(false); //move the cursor to the first unprotected field rw.setCursorPosition((short) 0); rw.setCursorPosition(rw.getNextUnprotectedField(rw.getCursorPosition())); //TO-DO reset the AID } } /** *

3.5.4 Write Structured Field

* WSF is used to send structured fields from the spplication program to the display. On the application-to-display flow [outbound], * structured fields can be sent only with the WSF command. *

* The format of a WSF data stream is as follows: *

*
WSF CommandStructured FieldStructured Field...
*

* In our case, we're really only concerned with responding to queries, so we can inform the host of our capabilities on demand. Query * replies are covered in agonizing detail in chapter 6 of the * 3270 Data Stream Programmer's Reference. Suffice it to say That we're telling the host: *
  • How big our screen is
  • *
  • How many colors we support
  • *
  • Do we handle outlining
  • */ private synchronized void writeStructuredField(short[] buf) { log.finer("Write Structured Field..."); int cmnd; int length; int offset; int nleft; int pid; int sfid; int type; int buflen; int i; int n; buflen = buf.length; offset = 1; nleft = buflen - 1; while (nleft > 0) { if (nleft < 3) { return; //WSF too small } length = (buf[offset] << 8) + buf[offset + 1]; sfid = buf[offset + 2]; switch (sfid) { case SF_READ_PART: /* * Read Partion - p. 5-47 */ if (length < 5) { return; //WSF-RP too small } pid = buf[offset + 3]; type = buf[offset + 4]; /* Check to see if it is a Query 0x02 */ switch (type) { case SF_RP_QUERY: if (pid != 0xFF) { return; } try { short[] queryReply = buildQueryReply(2, true); rw.getTelnet().sendData(queryReply, queryReply.length); } catch (IOException e) { log.severe(e.getMessage()); } break; case SF_RP_QLIST: if (pid != 0xFF) { return; } switch (buf[offset + 5]) { case SF_RPQ_LIST: log.fine("List"); return; case SF_RPQ_EQUIV: log.fine("Equivalent+List"); return; case SF_RPQ_ALL: log.fine("All"); try { short[] queryReply = buildQueryReply(2, true); rw.getTelnet().sendData(queryReply, queryReply.length); } catch (IOException e) { log.severe(e.getMessage()); } break; } break; default: return; } break; case SF_RPQ_EQUIV: /* * Outbound 3270DS - p. 5-41 */ if (length < 5) { return; //WSF-OBDS too small } pid = buf[offset + 3]; cmnd = buf[offset + 4]; if (pid != 0x00) { return; //WSF-OBDS invalid PID } switch (cmnd) { case CMD_W_EBCDIC: case CMD_EW_EBCDIC: case CMD_EWA_EBCDIC: case CMD_EAU_EBCDIC: n = length - 4; dataIn = new short[n]; for (i = 0; i < n; ++i) { dataIn[i] = buf[i + 4]; } writeOperation(); break; default: return; //WSF-OBDS unsupported } break; default: return; //unsupported WFS ID } offset += length; nleft -= length; } } /** *

    3.6 Read Operations

    * The process of sending data inbound is called a read operation. A read operation can be initiated by the following: *
      *
    • The host application sending an explicit read command
    • *
    • The host application program sending a Read Partition structured field specifying Read Buffer, Read Modified, or Read Modified * All.
    • *
    • An operator action, for example, pressing the Enter key.
    • *
    * A read operation sends an inbound data stream (from the terminal to the application program) with an AID byte as the first byte of * the inbound data stream. The inbound data stream usually consists of an AID followed by the cursor address (2 bytes). These 3 bytes * of the read data stream the AID, and cursor address are known as the read heading. The inbound data stream format is as * follows: *

    *

    * * *
    Byte 1Byte 2Byte 3Byte 4
    AIDCursor Address (2 bytes)Data
    *

    *

    3.6.1 Read Commands

    * Three read commands can be sent by the application program: Read Buffer Read Modified, and Read Modified All. * 3.6.1.1 Read Buffer Command * Operation of the Read Buffer command causes all data in the addressed display buffer, from the buffer location at which reading * starts through the last buffer location, to be transmitted to the host. For displays the transfer of data begins from buffer address * 0. */ private synchronized void readBuffer() { int byteCount = 0; short[] dataOut = new short[((chars.length) * 2) + 40]; //get the current AID dataOut[byteCount++] = rw.getAIDValue(); //convert the current cursor position to 14-bit addressing dataOut[byteCount++] = addrTable[(rw.getCursorPosition() >> 6) & 0x3F]; dataOut[byteCount++] = addrTable[rw.getCursorPosition() & 0x3F]; //iterate through the screen buffer, if a position //contains an FA send it instead of the character. for (int i = 0; i < (chars.length); i++) { Term3270Char currChar = chars[i]; if (currChar.isStartField()) { dataOut[byteCount++] = ORDER_SF; dataOut[byteCount++] = currChar.getFieldAttribute(); } else { dataOut[byteCount++] = (short) asc2ebc[currChar.getChar()]; } } try { rw.getTelnet().sendData(dataOut, byteCount); } catch (IOException e) { } } /** *

    3.6.2.1 Read Modified Operation

    * During a read modified operation, if an AID other than selector pen attention, cursor select key, PAkey, or Clear key is generated, * all fields that have been modified by keyboard, selector pen, or magnetic reader activity are transferred to the application program. * A major feature of the read modified operation is null suppression. Only non-null character data and corresponding character * attribute data (in Character mode)are transmitted. All null character data and all extended attributes for null character data are * suppressed. *

    * If a space or null selector pen AID is generated, fields are not transferred to main storage during the read modified operation. * Instead, when a set MDTbit is found (indicating selector pen and/or keyboard activity), only the read heading, the SBA order code, * and the attribute address +1 are transferred. *

    * If the buffer is unformatted (contains no fields), the read data stream consists of the 3-byte read heading followed by all * alphanumeric data in the buffer (nulls are suppressed), even when part or all of the data has not been modified. Since an unformatted * buffer contains no attribute bytes no SBA codes with associated addresses or address characters included in the data stream, and the * modification of data cannot be determined. Data transfer starts at address 0 and continues to the end of the buffer. At the end of * the operation, the buffer address is set to 0. */ protected synchronized void readModified() { rw.setKeyboardLocked(true); int byteCount = 0; short[] dataOut = new short[(chars.length * 2) + 40]; dataOut[byteCount++] = rw.getAIDValue(); switch (rw.getAIDEnum()) { case OHIO_AID_3270_PA1: case OHIO_AID_3270_PA2: case OHIO_AID_3270_PA3: case OHIO_AID_3270_CLEAR: try { rw.getTelnet().sendData(dataOut, byteCount); } catch (Exception e) { } return; } //cursor position dataOut[byteCount++] = addrTable[(rw.getCursorPosition() >> 6) & 0x3F]; dataOut[byteCount++] = addrTable[rw.getCursorPosition() & 0x3F]; //are there any fields? (formatted/unformatted) if (fields.size() == 0) { for (int i = 0; i < chars.length; i++) { Term3270Char currChar = chars[i]; if (currChar.getChar() != 0) //null suppression { dataOut[byteCount++] = (short) asc2ebc[currChar.getChar()]; } } try { rw.getTelnet().sendData(dataOut, byteCount); } catch (IOException e) { } bufferAddr = 0; return; } //get an enumeration of the current fields Enumeration e = fields.elements(); //iterate through the fields, checking for modification while (e.hasMoreElements()) { Term3270Field f = (Term3270Field) e.nextElement(); if (f.isModified()) { //field has been modified... get characters stored in the //field. TermChar[] fieldChars = f.getChars(); //send an SBA on the beginning of this field + 1 //(ignore the field attribute) dataOut[byteCount++] = ORDER_SBA; dataOut[byteCount++] = addrTable[((f.getBeginBA() + 1) >> 6) & 0x3F]; dataOut[byteCount++] = addrTable[(f.getBeginBA() + 1) & 0x3F]; //put the characters in the output buffer for (int i = 1; i < fieldChars.length; i++) { if (fieldChars[i].getChar() != 0) //null suppression { dataOut[byteCount++] = (short) asc2ebc[fieldChars[i].getChar()]; //System.out.print("Hey..." + fieldChars.length + fieldChars[i].getChar()); } } } } try { //System.out.println("Sending data..."); rw.getTelnet().sendData(dataOut, byteCount); } catch (IOException ioe) { log.warning("exception in readModified: " + ioe.getMessage()); } } private synchronized void readModifiedAll() { readModified(); } private void beep() { //TODO: Add beep code here... use a callback interface to the consumer. //System.out.println("Beep.."); log.fine("beep"); rw.getClient().beep(); } /** *

    4.3.1 Start Field(SF)

    * The SF order indicates the start of a field. *

    Table 4-4 - Bit Definitions for 3270 Field Attributes

    * * * * * * * * * */ private synchronized void startField() { //increment the buffer address, and clear the existing character chars[bufferAddr].clear(); chars[bufferAddr].setStartField(); chars[bufferAddr].setFieldAttribute(dataIn[++counter]); rw.setFieldsChanged(true); if (++bufferAddr == chars.length) { bufferAddr = 0; } } /** *

    4.3.2 Start Field Extended (SFE)

    * The SFE order is also used to indicate the start of a field. However, the SFE control sequence contains information on the field's * properties that are described in the extended field attribute. The SFE order has the following format: *
    BitDescription
    0, 1N/A
    20 - Field is Unprotected
    * 1 - Field is Protected
    30 - Alphanumeric
    * 1 - Numeric
    4, 500 - Display/not selector pen detectable
    * 01 - Display/selector pen detectable
    * 10 - Intensified display/selector pen detectable(BOLD)
    * 11 - Nondisplay, nondetectable (PASSWORDS, etc.)
    6Reserved. Must Always be 0
    7MDT identifies modified fields during Read Modified Command operations.
    * 0 - Field has not been modified.
    * 1 - Field has been modified by the operator.
    * *
    0x29Number of Attribute Type-Value pairsAttribute TypeAttribute Value
    */ private synchronized void startFieldExtended() { counter++; chars[bufferAddr].clear(); // Hard-learned lesson: if no StartField is specified, // but extended attributes have been defined, you must // define a default start field. chars[bufferAddr].setStartField(); chars[bufferAddr].setFieldAttribute((short) 0x00); rw.setFieldsChanged(true); int pairs = dataIn[counter]; //get the number of attribute type pairs for (int i = 0; i < pairs; i++) { //System.out.println("SFE: " + Integer.toHexString(dataIn[++counter])); int att = dataIn[++counter]; counter++; switch (att) { // get the next value from dataIn which will tell us what kind of attribute it is case XA_SF: // same as SF command above chars[bufferAddr].setFieldAttribute(dataIn[counter]); break; case XA_VALIDATION: chars[bufferAddr].setValidation(dataIn[counter]); break; case XA_OUTLINING: chars[bufferAddr].setOutlining(dataIn[counter]); break; case XA_HIGHLIGHTING: chars[bufferAddr].setHighlighting(dataIn[counter]); break; case XA_FGCOLOR: chars[bufferAddr].setForeground(dataIn[counter]); break; case XA_CHARSET: //not supported - nightmare break; case XA_BGCOLOR: chars[bufferAddr].setBackground(dataIn[counter]); break; case XA_TRANSPARENCY: //not supported - What does it do? break; } } bufferAddr++; } /** *

    4.3.3 Set Buffer Address

    * The Set Buffer Address function converts a two-byte segment into an integer corresponding to the buffer address. If the first 2 bits * of the first byte are 00, it signals that a 14-bit binary address follos (the remaining 6 bits of byte 1 and 8 bits of byte 2). This * is easily arrived at by shifting the first byte 8 positions left and adding the second byte. If the first two bits of the first byte * contain any other bit pattern (01, 10, 11), the two bytes comprise a 12-bit coded address which can be arrived at by * ((counter1 & 0x3F) << 6) + (counter2 & 0x3F) */ private synchronized int setBufferAddress() { return setBufferAddress(dataIn[++counter], dataIn[++counter]); } public static int setBufferAddress(int counter1, int counter2) { if ((counter1 & 0xC0) == 0x00) { return ((counter1 & 0x3F) << 8) + counter2; } else { return ((counter1 & 0x3F) << 6) + (counter2 & 0x3F); } } /** *

    4.3.4 Set Attribute(SA)

    * The SA order is used to specify a character's attribute type and its value so that subsequently interpreted characters in the data * stream apply the character properties defined by the type-value pair. The format of the SA control sequence is as follows: * * * * *
    0x28Attribute TypeAttribute Value
    */ private synchronized void setAttribute() { int att = dataIn[++counter]; rw.setFieldsChanged(true); counter++; //System.out.println("IBM SA: " + att); switch (att) { case 0: foreground = Term3270Char.FGCOLOR_DEFAULT; background = Term3270Char.BGCOLOR_DEFAULT; highlight = Term3270Char.HL_NORMAL; break; case XA_HIGHLIGHTING: highlight = dataIn[counter]; break; case XA_FGCOLOR: foreground = dataIn[counter]; break; case XA_BGCOLOR: background = dataIn[counter]; break; } } /** *

    4.3.5 Modify Field (MF)

    * The MF order begins a sequence that updates field and extended field attributes at the current buffer address. After the attributes * have been updated, the current buffer address is incremented by one. *

    * The MF control sequence has the following format: *

    * * * * * *
    0x2CNumber of Attribute Type/Value PairsAttribute TypeAttribute Value
    * Gotchas:
    * *
      *
    • Attribute types not specified remain unchanged
    • *
    • If the current buffer address is not a field attribute, the MF order should be rejected
    • *
    */ private synchronized void modifyField() { // reject if not a FA Term3270Char currChar = chars[bufferAddr]; //System.out.println(" " + currChar.isStartField()); if (!currChar.isStartField()) { return; } int pairs = dataIn[++counter]; for (int i = 0; i < pairs; i++) { //System.out.println("Attribute to modify: " + Integer.toHexString(dataIn[++counter])); int att = dataIn[++counter]; counter++; switch (att) { case ORDER_SFE: case ORDER_SF: case XA_SF: currChar.setFieldAttribute(dataIn[counter]); break; case XA_VALIDATION: currChar.setValidation(dataIn[counter]); break; case XA_HIGHLIGHTING: currChar.setHighlighting(dataIn[counter]); break; case XA_FGCOLOR: currChar.setForeground(dataIn[counter]); break; case XA_BGCOLOR: currChar.setBackground(dataIn[counter]); break; case XA_OUTLINING: currChar.setOutlining(dataIn[counter]); break; } } bufferAddr++; } /** *

    4.3.6 Insert Cursor (IC)

    * The IC order repositions the cursor to the locations specified by the current buffer address. Execution of this order does not change * the current buffer address. */ private synchronized void insertCursor() { rw.setCursorPosition((short) bufferAddr); } /** *

    4.3.7 Program Tab (PT)

    * The PT order advances the current buffer address to the address of the first character position of the next unprotected field. If PT * is issued when the current buffer address is the location of a field attribute of an unprotected field, the buffer advances to the * next location of that field (one location). In addition, if PT does not immediately follow a command order, or order sequence (such * as after the WCC, IC, and RA respectively), nulls are inserted in the buffer from the current buffer address to the end of the field, * regardless of the value of bit 2 (protected/unprotected) of the field attribute for the field. When PT immediately follows a command, * order, or order sequence, the buffer is not modified. */ private synchronized void programTab() { log.finer("Program Tab..."); int newAddr; int oldAddr = bufferAddr; newAddr = rw.getNextUnprotectedField(bufferAddr); //System.out.println("next unprotected: " + newAddr); if (newAddr <= bufferAddr) { bufferAddr = 0; } else { bufferAddr = newAddr; } if (!lastWasCommand) { //if bufferAddr = 0, there's no more FAs so //clear from here. //if(!chars[oldAddr].getField().setProtected()) // newAddr = oldAddr; //else Term3270Char currChar = null; while ((oldAddr < chars.length) && !(currChar = chars[oldAddr]).isStartField()) { currChar.setChar((char) 0); oldAddr++; } //System.out.println("Buffer Address.." + bufferAddr); /* newAddr = (bufferAddr == 0)?oldAddr:bufferAddr; System.out.println("Get field..." + newAddr); int end = 0; try { end = chars[oldAddr].getField().getEndBA(); } catch(NullPointerException e) { e.printStackTrace(); } System.out.println("Clearing: " + oldAddr + " to " + end); for(int c = oldAddr; c <= end; c++) { try { chars[c].clear(); display[c] = ' '; } catch(ArrayIndexOutOfBoundsException e){} } */ } } /** *

    4.3.8 Repeat to Address (RA)

    * The RA order stores a specified character in all character buffer locations, starting at the current bufer address and ending at (but * not including) the specified stop address. */ private synchronized void repeatToAddress() { //counter++; int address = setBufferAddress(); int charIn = dataIn[++counter]; char c = (char) ebc2asc[charIn]; while (bufferAddr != address) { Term3270Char currChar = chars[bufferAddr]; currChar.clear(); //currChar.setHighlighting(highlight); //currChar.setForeground(foreground); //currChar.setBackground(background); currChar.setChar(c); if (++bufferAddr > (chars.length - 1)) { bufferAddr = 0; } } } /** *

    4.3.9 Erase Unprotected to Address (EUA)

    * The EUA Order stores nulls in all unprotected character locations, starting at the current buffer address and ending at, but not * including, the specified stop address. */ private synchronized void eraseUnprotectedToAddress() { //counter++; int address = setBufferAddress(); if (address == bufferAddr) { eraseAllUnprotected(); } while (bufferAddr < address) { Term3270Char currChar = chars[bufferAddr]; if (!currChar.isProtected()) { currChar.setChar((char) 0); if (currChar.isStartField()) { currChar.setFieldAttribute(TermChar.FieldAttribute.MODIFIED, false); } } if (++bufferAddr > (chars.length - 1)) { bufferAddr = 0; } } } private synchronized void graphicEscape() { //not supported counter++; } private synchronized void buildFields() { rw.buildFields(true); } /** * This method builds the reply packet to send to the host, it contains our capabilities as a 3270 host. * */ private synchronized short[] buildQueryReply(int model, boolean summary) { /* * We have several capabilities which we need to report * 1. Color * 2. Highlighting * 3. Partition */ final short HIGHLIGHT_DEFAULT = 0x00; final short HIGHLIGHT_NORMAL = 0xF0; final short HIGHLIGHT_BLINK = 0xF1; final short HIGHLIGHT_REVERSE = 0xF2; final short HIGHLIGHT_UNDERSCORE = 0xF4; final short HIGHLIGHT_INTENSIFY = 0xF8; /* Colors: Listed in 6.13.3 */ final short COLOR_NEUTRAL1 = 0x00; final short COLOR_BLUE = 0xF1; final short COLOR_RED = 0xF2; final short COLOR_PINK = 0xF3; final short COLOR_GREEN = 0xF4; final short COLOR_TURQUOISE = 0xF5; final short COLOR_YELLOW = 0xF6; final short COLOR_NEUTRAL2 = 0xF7; final short COLOR_BLACK = 0xF8; final short COLOR_DEEP_BLUE = 0xF9; final short COLOR_ORANGE = 0xFA; final short COLOR_PURPLE = 0xFB; final short COLOR_PALE_GREEN = 0xFC; final short COLOR_PALE_TURQUOISE = 0xFD; final short COLOR_GREY = 0xFE; final short COLOR_WHITE = 0xFF; /* Highlighting */ short[] highlightReply = new short[15]; /* Bytes 0-1 Length of the Structured Field */ highlightReply[0] = (short) 0x00; highlightReply[1] = (short) 0x0F; /* Byte 2 Query Reply */ highlightReply[2] = QUERY_REPLY; /* Byte 3 Highlighting */ highlightReply[3] = HIGHLIGHT_QUERY_REPLY; /* Byte 4 Number of attribute-value/action pairs */ highlightReply[4] = (short) 0x05; /* Part 1: Data stream attribute value accepted */ /* Part 2: Data stream action */ /* Pair 1 */ highlightReply[5] = HIGHLIGHT_DEFAULT; highlightReply[6] = HIGHLIGHT_NORMAL; /* Pair 2 */ highlightReply[7] = HIGHLIGHT_BLINK; highlightReply[8] = HIGHLIGHT_BLINK; /* Pair 3 */ highlightReply[9] = HIGHLIGHT_REVERSE; highlightReply[10] = HIGHLIGHT_REVERSE; /* Pair 4 */ highlightReply[11] = HIGHLIGHT_UNDERSCORE; highlightReply[12] = HIGHLIGHT_UNDERSCORE; /* Pair 5 */ highlightReply[13] = HIGHLIGHT_INTENSIFY; highlightReply[14] = HIGHLIGHT_INTENSIFY; /* Color */ short[] colorReply = new short[40]; /* Bytes 0-1 Length of the Structured Field */ colorReply[0] = (short) 0x00; colorReply[1] = (short) 0x26; colorReply[2] = QUERY_REPLY; colorReply[3] = COLOR_QUERY_REPLY; colorReply[4] = (short) 0x00; /* Number of Pairs */ colorReply[5] = (short) 0x10; /* Pair 1 */ colorReply[6] = COLOR_NEUTRAL1; colorReply[7] = COLOR_GREEN; /* Pair 2 */ colorReply[8] = COLOR_BLUE; colorReply[9] = COLOR_BLUE; /* Pair 3 */ colorReply[10] = COLOR_RED; colorReply[11] = COLOR_RED; /* Pair 4 */ colorReply[12] = COLOR_PINK; colorReply[13] = COLOR_PINK; /* Pair 5 */ colorReply[14] = COLOR_GREEN; colorReply[15] = COLOR_GREEN; /* Pair 6 */ colorReply[16] = COLOR_TURQUOISE; colorReply[17] = COLOR_TURQUOISE; /* Pair 7 */ colorReply[18] = COLOR_YELLOW; colorReply[19] = COLOR_YELLOW; /* Pair 8 */ colorReply[20] = COLOR_NEUTRAL2; colorReply[21] = COLOR_NEUTRAL2; /* Pair 9 */ colorReply[22] = COLOR_BLACK; colorReply[23] = COLOR_BLACK; /* Pair 10 */ colorReply[24] = COLOR_DEEP_BLUE; colorReply[25] = COLOR_DEEP_BLUE; /* Pair 11 */ colorReply[26] = COLOR_ORANGE; colorReply[27] = COLOR_ORANGE; /* Pair 12 */ colorReply[28] = COLOR_PURPLE; colorReply[29] = COLOR_PURPLE; /* Pair 13 */ colorReply[30] = COLOR_PALE_GREEN; colorReply[31] = COLOR_PALE_GREEN; /* Pair 14 */ colorReply[32] = COLOR_PALE_TURQUOISE; colorReply[33] = COLOR_PALE_TURQUOISE; /* Pair 15 */ colorReply[34] = COLOR_GREY; colorReply[35] = COLOR_GREY; /* Pair 16 */ colorReply[36] = COLOR_WHITE; colorReply[37] = COLOR_WHITE; /* Pair 17 */ colorReply[38] = COLOR_WHITE; colorReply[39] = COLOR_WHITE; /* Implicit Partition. See 6.31.2 */ short[] partitionReply = new short[17]; /* Bytes 0-1 Length */ partitionReply[0] = (short) 0x00; partitionReply[1] = (short) 0x11; partitionReply[2] = (short) QUERY_REPLY; /* Byte 3 QCODE Identifier */ partitionReply[3] = (short) IMP_PART_QUERY_REPLY; /* Bytes 4-5 Reserved */ partitionReply[4] = (short) 0x00; partitionReply[5] = (short) 0x00; /* 6.31.3 Implicit Partition Sizes for Display Devices Self-Defining Parameter */ partitionReply[6] = (short) 0x0B; partitionReply[7] = (short) 0x01; partitionReply[8] = (short) 0x00; /* Bytes 9-10 Width of the Implicit Partition default screen size (in character cells) */ partitionReply[9] = (short) 0x00; partitionReply[10] = (short) 0x50; /* Bytes 11-12 Height of the Implicit Partition default screen size */ partitionReply[11] = (short) 0x00; partitionReply[12] = (short) 0x18; /* FIXME The alternate size should be the dimensions of the terminal model selected */ /* Bytes 13-14 Width of the Implicit Partition alternate screen size */ partitionReply[13] = (short) 0x00; partitionReply[14] = (short) 0x50; /* Bytes 15-16 Height of the Implicit Partition alternate screen size */ partitionReply[15] = (short) 0x00; partitionReply[16] = (short) 0x18; /* Summary */ short[] summaryReply = new short[8]; summaryReply[0] = (short) 0x00; summaryReply[1] = (short) 0x08; /* Byte 2 Query Reply */ summaryReply[2] = QUERY_REPLY; /* Byte 3 Summary Query Reply */ summaryReply[3] = SUMMARY_QUERY_REPLY; /* These are our capabilities... * Kind of silly to indicate we're capable of a summary reply * in a summary reply...that's how it works though. */ summaryReply[4] = SUMMARY_QUERY_REPLY; summaryReply[5] = COLOR_QUERY_REPLY; summaryReply[6] = HIGHLIGHT_QUERY_REPLY; summaryReply[7] = IMP_PART_QUERY_REPLY; /* Assembly of the Reply Packet */ /* Create a buffer the length of each of the member pieces, plus the header and footer */ int qReplyLength = 1 + summaryReply.length + highlightReply.length + colorReply.length + partitionReply.length; /* Initialize the queryReply packet buffer */ short[] queryReply = new short[qReplyLength]; queryReply[0] = 0x88; int bufPos = 1; /* Add the summary Capability */ for (int i = 0; i < summaryReply.length; i++) { queryReply[bufPos] = summaryReply[i]; bufPos++; } /* Add the Color Capability */ for (int i = 0; i < colorReply.length; i++) { queryReply[bufPos] = colorReply[i]; bufPos++; } /* Add the Highlight Capability */ for (int i = 0; i < highlightReply.length; i++) { queryReply[bufPos] = highlightReply[i]; bufPos++; } /* Add the Partition Capability */ for (int i = 0; i < partitionReply.length; i++) { queryReply[bufPos] = partitionReply[i]; bufPos++; } /* for(int i = 0; i < queryReply.length; i++ ) { String myStr = Long.toHexString(new Short( queryReply[i] ).longValue()); if( myStr.length() == 1 ) { myStr = "0" + myStr; } System.out.print( myStr ); }*/ return queryReply; } @Override public void Fkey(Ohio.OHIO_AID aid) { readModified(); } public void status(int msg) { rw.getClient().status(msg); } public void broadcastMessage(String msg) { rw.getClient().broadcastMessage(msg); } public String getTermType() { return rw.getTermType(); } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy