Fix YMS bug
Build and Test j3270 / Build JAR & Run Tests (push) Successful in 43s
Release j3270 / Build & Publish Release (push) Successful in 43s

This commit is contained in:
2026-08-20 19:58:46 -04:00
parent 10a31476c2
commit f0fc1b6e70
13 changed files with 133 additions and 43 deletions
@@ -6,6 +6,7 @@ import org.lib3270j.charset.EbcdicTranslator;
import org.lib3270j.listener.ScreenUpdateListener;
import static org.lib3270j.protocol.DS3270Constants.*;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;
@@ -451,8 +452,8 @@ public class DataStreamProcessor {
pos += 4;
}
// Fill from current position to target
while (baddr != toAddr) {
// Fill from current position to target (do-while wraps entire buffer if baddr == toAddr)
do {
ExtendedAttribute ea = screen.getCell(baddr);
ea.fa = 0; // Destroy previous field attribute if any
ea.ec = (byte) fillChar;
@@ -461,7 +462,7 @@ public class DataStreamProcessor {
ea.gr = (byte) currentGr;
ea.cs = fillCs;
baddr = (baddr + 1) % size;
}
} while (baddr != toAddr);
screen.setBufferAddress(baddr);
lastWasOrder = true;
break;
@@ -476,7 +477,7 @@ public class DataStreamProcessor {
if (toAddr >= size)
toAddr = toAddr % size;
while (baddr != toAddr) {
do {
ExtendedAttribute ea = screen.getCell(baddr);
if (!ea.isFieldAttribute()) {
int faAddr = screen.findFieldAttribute(baddr);
@@ -491,7 +492,7 @@ public class DataStreamProcessor {
}
}
baddr = (baddr + 1) % size;
}
} while (baddr != toAddr);
screen.setBufferAddress(baddr);
pos += 3;
lastWasOrder = true;
@@ -637,7 +638,13 @@ public class DataStreamProcessor {
outputPos = 0;
int aid = AID_NO; // Last AID
int aid = (inputProcessor != null && inputProcessor.getLastAid() != 0)
? inputProcessor.getLastAid()
: AID_NO;
if (inputProcessor != null) {
inputProcessor.setLastAid(AID_NO);
}
int size = screen.getRows() * screen.getCols();
// AID byte
@@ -649,31 +656,56 @@ public class DataStreamProcessor {
outputWrite(caddr[1] & 0xFF);
if (!screen.isFormatted()) {
// Unformatted: send everything
if (all) {
for (int i = 0; i < size; i++) {
outputWrite(screen.getCell(i).ec & 0xFF);
// Unformatted screen: send data up to last non-null
int lastNonNull = -1;
for (int i = size - 1; i >= 0; i--) {
if (screen.getCell(i).ec != 0) {
lastNonNull = i;
break;
}
}
if (lastNonNull >= 0) {
for (int i = 0; i <= lastNonNull; i++) {
int b = screen.getCell(i).ec & 0xFF;
outputWrite(b != 0 ? b : 0x40);
}
}
} else {
// Formatted: send modified fields
// Formatted screen: send modified fields with trailing NULLs stripped
for (int i = 0; i < size; i++) {
ExtendedAttribute ea = screen.getCell(i);
if (ea.isFieldAttribute() && (all || faIsModified(ea.fa & 0xFF))) {
int fieldStart = (i + 1) % size;
// Send SBA for field start
// Collect field data and find last non-null byte
ByteArrayOutputStream fieldData = new ByteArrayOutputStream();
int pos = fieldStart;
int lastNonNull = -1;
int fieldLen = 0;
while (!screen.getCell(pos).isFieldAttribute()) {
int b = screen.getCell(pos).ec & 0xFF;
fieldData.write(b);
if (b != 0x00) {
lastNonNull = fieldLen;
}
fieldLen++;
pos = (pos + 1) % size;
if (pos == fieldStart)
break;
}
// Always send SBA and address
outputWrite(ORDER_SBA);
byte[] addr = encodeAddress(fieldStart, screen.getRows(), screen.getCols());
outputWrite(addr[0] & 0xFF);
outputWrite(addr[1] & 0xFF);
// Send field contents until next FA
int pos = fieldStart;
while (pos < size && !screen.getCell(pos).isFieldAttribute()) {
outputWrite(screen.getCell(pos).ec & 0xFF);
pos = (pos + 1) % size;
if (pos == fieldStart)
break;
// Send field data (strip trailing nulls)
if (lastNonNull >= 0) {
byte[] allData = fieldData.toByteArray();
for (int k = 0; k <= lastNonNull; k++) {
outputWrite(allData[k] & 0xFF);
}
}
}
}
@@ -265,20 +265,21 @@ public class QueryReplyBuilder {
private byte[] buildImplicitPartition(int maxCols, int maxRows) {
ByteArrayOutputStream out = new ByteArrayOutputStream(22);
// Implicit partition sizes, 2 self-defining parameters
// Implicit partition sizes SDP (Self-Defining Parameter)
// SDP 1: Default screen size
out.write(0x00); // flags
out.write(0x00); // reserved
out.write(0x0b); // SDP length
// SDP 1: Implicit partition sizes
out.write(0x00); // SDP length high byte
out.write(0x0b); // SDP length low byte (11 bytes: 2 len + 1 type + 1 res + 8 dims)
out.write(0x01); // SDP type: implicit partition sizes
out.write(0x00); // reserved
// Default
// Default dimensions (Model 2: 80x24)
out.write((MODEL_2_COLS >> 8) & 0xFF);
out.write(MODEL_2_COLS & 0xFF);
out.write((MODEL_2_ROWS >> 8) & 0xFF);
out.write(MODEL_2_ROWS & 0xFF);
// Alternate
// Alternate dimensions
out.write((maxCols >> 8) & 0xFF);
out.write(maxCols & 0xFF);
out.write((maxRows >> 8) & 0xFF);
@@ -138,9 +138,10 @@ public class InputProcessor {
}
// Advance cursor
int startAdvance = baddr;
baddr = (baddr + 1) % size;
// Skip over field attributes
while (screen.getCell(baddr).isFieldAttribute()) {
// Skip over field attributes safely
while (screen.getCell(baddr).isFieldAttribute() && baddr != startAdvance) {
baddr = (baddr + 1) % size;
}
screen.setCursorAddress(baddr);
@@ -255,18 +256,26 @@ public class InputProcessor {
}
}
} else {
// Unformatted screen in 3270 mode:
// Send character data from address 0 up to last non-null character
int size = screen.getRows() * screen.getCols();
int lastNonNull = -1;
for (int i = size - 1; i >= 0; i--) {
if (screen.getCell(i).ec != 0) {
lastNonNull = i;
// Unformatted screen in 3270 mode (e.g. line-mode console):
// Send AID + cursor address + only the active input line (row containing cursor)
int cols = screen.getCols();
int curAddr = screen.getCursorAddress();
int row = curAddr / cols;
int rowStart = row * cols;
int rowEnd = rowStart + cols;
// Find last non-null, non-blank character on the current line
int lastChar = rowStart - 1;
for (int i = rowEnd - 1; i >= rowStart; i--) {
int ec = screen.getCell(i).ec & 0xFF;
if (ec != 0x00 && ec != 0x40) {
lastChar = i;
break;
}
}
if (lastNonNull >= 0) {
for (int i = 0; i <= lastNonNull; i++) {
if (lastChar >= rowStart) {
for (int i = rowStart; i <= lastChar; i++) {
int b = screen.getCell(i).ec & 0xFF;
out.write(b != 0 ? b : 0x40);
}
@@ -325,6 +334,13 @@ public class InputProcessor {
screen.setCursorAddress(addr);
}
public int getLastAid() { return lastAid; }
public void setLastAid(int aid) { this.lastAid = aid; }
public void setCursorAddress(int baddr) {
screen.setCursorAddress(baddr);
}
public void backTab() {
// Find previous unprotected field
int addr = screen.getCursorAddress();
@@ -132,10 +132,14 @@ public class TelnetConnection {
if (log.isLoggable(Level.FINE)) {
log.fine("RCVD " + n + " bytes: " + formatHex(buf, 0, n));
}
for (int i = 0; i < n; i++) {
fsm.feedByte(buf[i] & 0xFF);
try {
for (int i = 0; i < n; i++) {
fsm.feedByte(buf[i] & 0xFF);
}
fsm.endOfNetworkData();
} catch (Throwable t) {
log.log(Level.SEVERE, "Exception processing incoming data stream", t);
}
fsm.endOfNetworkData();
}
}
} catch (SocketException e) {
@@ -148,6 +152,11 @@ public class TelnetConnection {
log.log(Level.WARNING, "Read error", e);
fsm.onError("Read error: " + e.getMessage());
}
} catch (Throwable t) {
if (running) {
log.log(Level.SEVERE, "Unexpected fatal error in readLoop", t);
fsm.onError("Network loop error: " + t.getMessage());
}
}
}