From ff5dfda34379457931ed5f45ce501bca00c48a1a Mon Sep 17 00:00:00 2001 From: Rudi Date: Thu, 27 Aug 2026 22:42:40 -0400 Subject: [PATCH] IND testing --- .../datastream/DataStreamProcessor.java | 4 +- .../haus/nightmare/lib3270j/ft/FTCut.java | 4 ++ .../haus/nightmare/lib3270j/ft/FTDft.java | 5 +- .../haus/nightmare/lib3270j/ft/FTCutTest.java | 64 +++++++++++++++++++ .../haus/nightmare/lib3270j/ft/FTDftTest.java | 34 ++++++++++ 5 files changed, 108 insertions(+), 3 deletions(-) diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/DataStreamProcessor.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/DataStreamProcessor.java index 4a7343f..6bedaf2 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/DataStreamProcessor.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/DataStreamProcessor.java @@ -694,8 +694,8 @@ public class DataStreamProcessor { // ========== Read Modified ========== private void processReadModified(boolean all) { - if (ftDft != null) { - ftDft.readModified(); + if (ftDft != null && ftDft.readModified()) { + return; } outputPos = 0; diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTCut.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTCut.java index e187e23..541f334 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTCut.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTCut.java @@ -516,6 +516,10 @@ public class FTCut { if (!config.isAscii() || !config.isRemapFlag()) { ebc = localByte & 0xFF; + } else if (localByte < 0x20 || (localByte >= 0x80 && localByte < 0x9F)) { + ebc = localByte & 0xFF; + } else if (localByte == 0x9F) { + ebc = 0xFF; } else { int standardEbc = translator.unicodeToEbcdic((char) localByte); if (standardEbc < 0) { diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTDft.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTDft.java index 5e66d74..40abf08 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTDft.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTDft.java @@ -600,11 +600,14 @@ public class FTDft { /** * Handle a Read Modified command when upload data is pending. * Retransmits the last saved buffer. + * @return true if a buffer was retransmitted, false otherwise. */ - public void readModified() { + public boolean readModified() { if (dftSaveBuf != null && dftSaveBufLen > 0) { log.fine("DFT: Retransmitting saved buffer"); input.sendStructuredFieldData(dftSaveBuf); + return true; } + return false; } } diff --git a/lib3270j/src/test/java/haus/nightmare/lib3270j/ft/FTCutTest.java b/lib3270j/src/test/java/haus/nightmare/lib3270j/ft/FTCutTest.java index 4f6d251..c92ce84 100644 --- a/lib3270j/src/test/java/haus/nightmare/lib3270j/ft/FTCutTest.java +++ b/lib3270j/src/test/java/haus/nightmare/lib3270j/ft/FTCutTest.java @@ -113,4 +113,68 @@ public class FTCutTest { FTConstants.from6(screen.getCellEC(O_UP_LEN + 1), translator); assertTrue(len > 0); } + + @Test + public void testCutUploadAndDownloadRoundtripMultiLine() throws Exception { + FTConfig sendConfig = new FTConfig(); + sendConfig.setDirection(FTConfig.Direction.SEND); + sendConfig.setHostType(FTConfig.HostType.CMS); + sendConfig.setHostFilename("GENPASS REXX A"); + sendConfig.setTransferMode(FTConfig.TransferMode.ASCII); + sendConfig.setCrAction(FTConfig.CrAction.REMOVE); + sendConfig.setRemapFlag(true); + listener.config = sendConfig; + + StringBuilder sb = new StringBuilder(); + sb.append("/* REXX */\n"); + for (int i = 1; i <= 20; i++) { + sb.append("WORDS.").append(i).append(" = 'word_").append(i).append("'\n"); + } + String originalText = sb.toString(); + + ByteArrayInputStream in = new ByteArrayInputStream(originalText.getBytes(StandardCharsets.UTF_8)); + ftCut.initTransfer(in, null); + + // Setup CUT screen frame for DATA_REQUEST + screen.setCellFA(O_SF, (byte) (FA_PROTECT | FA_NUMERIC)); + screen.setCell(O_FRAME_TYPE, FT_DATA_REQUEST); + screen.setCell(O_DR_FRAME_SEQ, FTConstants.to6(0, translator)); + + ftCut.processScreenUpdate(); + + int len = (FTConstants.from6(screen.getCellEC(O_UP_LEN), translator) << 6) | + FTConstants.from6(screen.getCellEC(O_UP_LEN + 1), translator); + assertTrue(len > 0); + + byte[] rawScreenBytes = new byte[len]; + for (int i = 0; i < len; i++) { + rawScreenBytes[i] = (byte) screen.getCellEC(O_UP_DATA + i); + } + + // Test download decoding + FTConfig recvConfig = new FTConfig(); + recvConfig.setDirection(FTConfig.Direction.RECEIVE); + recvConfig.setHostType(FTConfig.HostType.CMS); + recvConfig.setHostFilename("GENPASS REXX A"); + recvConfig.setTransferMode(FTConfig.TransferMode.ASCII); + recvConfig.setCrAction(FTConfig.CrAction.REMOVE); + recvConfig.setRemapFlag(true); + listener.config = recvConfig; + + ByteArrayOutputStream downloadOut = new ByteArrayOutputStream(); + ftCut.initTransfer(null, downloadOut); + + // Setup CUT screen frame for DATA + screen.setCell(O_FRAME_TYPE, FT_DATA); + screen.setCell(O_DT_LEN, FTConstants.to6((len >> 6) & 0x3F, translator)); + screen.setCell(O_DT_LEN + 1, FTConstants.to6(len & 0x3F, translator)); + for (int i = 0; i < len; i++) { + screen.setCell(O_DT_DATA + i, rawScreenBytes[i] & 0xFF); + } + + ftCut.processScreenUpdate(); + + String downloaded = new String(downloadOut.toByteArray(), StandardCharsets.UTF_8); + assertEquals(originalText, downloaded); + } } diff --git a/lib3270j/src/test/java/haus/nightmare/lib3270j/ft/FTDftTest.java b/lib3270j/src/test/java/haus/nightmare/lib3270j/ft/FTDftTest.java index d635534..1e0b990 100644 --- a/lib3270j/src/test/java/haus/nightmare/lib3270j/ft/FTDftTest.java +++ b/lib3270j/src/test/java/haus/nightmare/lib3270j/ft/FTDftTest.java @@ -466,4 +466,38 @@ public class FTDftTest { String decodedStr = decodedSb.toString(); assertTrue(decodedStr.contains("WORDS.160 = 'word_160'\r\nWORDS.161 = 'word_161'\r\n")); } + + @Test + public void testDftReadModifiedRetransmit() { + assertFalse(ftDft.readModified()); + + FTConfig config = new FTConfig(); + config.setDirection(FTConfig.Direction.SEND); + config.setHostType(FTConfig.HostType.CMS); + config.setHostFilename("TEST FILE A"); + config.setTransferMode(FTConfig.TransferMode.ASCII); + config.setCrAction(FTConfig.CrAction.REMOVE); + config.setRemapFlag(true); + listener.config = config; + + String originalText = "LINE1\nLINE2\n"; + ByteArrayInputStream in = new ByteArrayInputStream(originalText.getBytes(StandardCharsets.UTF_8)); + ftDft.initTransfer(in, null); + + byte[] getReq = new byte[] { + (byte) 0x00, (byte) 0x05, (byte) SF_TRANSFER_DATA, + (byte) (TR_GET_REQ >> 8), (byte) (TR_GET_REQ & 0xFF) + }; + ftDft.processStructuredField(getReq, 0, getReq.length); + + assertEquals(1, inputProcessor.sentStructuredFields.size()); + byte[] firstSent = inputProcessor.sentStructuredFields.get(0); + + // Retransmit via readModified + assertTrue(ftDft.readModified()); + assertEquals(2, inputProcessor.sentStructuredFields.size()); + byte[] retransmitted = inputProcessor.sentStructuredFields.get(1); + + assertArrayEquals(firstSent, retransmitted); + } }