From a1b1273022e1a3efd29f08c5e87a3bb49cb92981 Mon Sep 17 00:00:00 2001 From: Rudi Date: Thu, 20 Aug 2026 18:24:35 -0400 Subject: [PATCH] Fix gradle and add workflows --- .gitea/issue_template/bug_report.yaml | 56 ++++++++ .gitea/issue_template/feature_request.yaml | 25 ++++ .gitea/workflows/build.yaml | 63 +++++++++ .github/workflows/build.yaml | 63 +++++++++ bugs.txt | 3 +- build.gradle | 37 ++---- build/MANIFEST.MF | 5 +- build/j3270/org/pubvm/j3270/J3270App$1.class | Bin 960 -> 960 bytes build/j3270/org/pubvm/j3270/J3270App$2.class | Bin 3088 -> 3088 bytes build/j3270/org/pubvm/j3270/J3270App.class | Bin 16841 -> 16836 bytes .../config/Settings$StartupBehavior.class | Bin 1286 -> 1286 bytes .../org/pubvm/j3270/config/Settings.class | Bin 9784 -> 9784 bytes .../org/pubvm/j3270/ui/ConnectDialog.class | Bin 5795 -> 5795 bytes .../org/pubvm/j3270/ui/SettingsDialog$1.class | Bin 1599 -> 1599 bytes .../org/pubvm/j3270/ui/SettingsDialog$2.class | Bin 1802 -> 1802 bytes .../org/pubvm/j3270/ui/SettingsDialog$3.class | Bin 665 -> 665 bytes .../org/pubvm/j3270/ui/SettingsDialog$4.class | Bin 1355 -> 1355 bytes .../org/pubvm/j3270/ui/SettingsDialog.class | Bin 21258 -> 21254 bytes .../org/pubvm/j3270/ui/StatusBar$1.class | Bin 1152 -> 1152 bytes .../j3270/org/pubvm/j3270/ui/StatusBar.class | Bin 4176 -> 4176 bytes .../org/pubvm/j3270/ui/TerminalPanel$1.class | Bin 2347 -> 2347 bytes .../org/pubvm/j3270/ui/TerminalPanel$2.class | Bin 689 -> 689 bytes .../org/pubvm/j3270/ui/TerminalPanel.class | Bin 22589 -> 24038 bytes .../org/lib3270j/ConnectionConfig.class | Bin 2493 -> 2493 bytes .../org/lib3270j/ConnectionState.class | Bin 2509 -> 2509 bytes .../org/lib3270j/DiagnosticClient$1.class | Bin 1559 -> 1559 bytes .../org/lib3270j/DiagnosticClient$2.class | Bin 1612 -> 1612 bytes .../org/lib3270j/DiagnosticClient.class | Bin 5432 -> 5432 bytes .../org/lib3270j/Telnet3270Client.class | Bin 6552 -> 6945 bytes .../lib3270j/org/lib3270j/TerminalModel.class | Bin 3598 -> 3598 bytes .../lib3270j/charset/EbcdicTranslator.class | Bin 3139 -> 3139 bytes .../DataStreamProcessor$OutputSender.class | Bin 375 -> 375 bytes .../datastream/DataStreamProcessor.class | Bin 14623 -> 14927 bytes .../datastream/QueryReplyBuilder.class | Bin 3971 -> 4792 bytes .../org/lib3270j/input/InputProcessor.class | Bin 7603 -> 8753 bytes .../listener/ConnectionListener.class | Bin 420 -> 420 bytes .../listener/ScreenUpdateListener.class | Bin 378 -> 378 bytes .../lib3270j/protocol/DS3270Constants.class | Bin 9902 -> 9902 bytes .../lib3270j/protocol/TN3270EConstants.class | Bin 4119 -> 4119 bytes .../lib3270j/protocol/TelnetConstants.class | Bin 3231 -> 3231 bytes .../lib3270j/screen/ExtendedAttribute.class | Bin 1298 -> 1298 bytes .../org/lib3270j/screen/ScreenBuffer.class | Bin 6938 -> 7189 bytes .../lib3270j/telnet/TelnetConnection.class | Bin 5634 -> 6087 bytes .../telnet/TelnetFSM$TN3270ESubmode.class | Bin 1287 -> 1287 bytes .../org/lib3270j/telnet/TelnetFSM.class | Bin 17208 -> 17208 bytes build_all.sh | 121 ++++++++++++++---- docs/gitea-runner.md | 59 +++++++++ gradlew | 2 + j3270/build.gradle | 14 +- .../java/org/pubvm/j3270/ft/FileTransfer.java | 21 ++- .../org/pubvm/j3270/ui/TerminalPanel.java | 55 ++++++++ .../java/org/lib3270j/Telnet3270Client.java | 12 ++ .../datastream/DataStreamProcessor.java | 14 +- .../datastream/QueryReplyBuilder.java | 60 ++++++++- .../main/java/org/lib3270j/ft/FTConfig.java | 78 ++++++----- .../src/main/java/org/lib3270j/ft/FTCut.java | 4 +- .../src/main/java/org/lib3270j/ft/FTDft.java | 4 +- .../org/lib3270j/input/InputProcessor.java | 77 +++++++++++ .../org/lib3270j/screen/ScreenBuffer.java | 19 +++ .../org/lib3270j/telnet/TelnetConnection.java | 27 +++- .../charset/EbcdicTranslatorTest.java | 66 ++++++++++ .../datastream/DataStreamProcessorTest.java | 69 ++++++++++ .../datastream/QueryReplyBuilderTest.java | 55 ++++++++ .../java/org/lib3270j/ft/FTConfigTest.java | 83 ++++++++++++ .../protocol/DS3270ConstantsTest.java | 51 ++++++++ .../org/lib3270j/screen/ScreenBufferTest.java | 64 +++++++++ run.sh | 47 ++----- settings.gradle | 1 - 68 files changed, 1102 insertions(+), 153 deletions(-) create mode 100644 .gitea/issue_template/bug_report.yaml create mode 100644 .gitea/issue_template/feature_request.yaml create mode 100644 .gitea/workflows/build.yaml create mode 100644 .github/workflows/build.yaml create mode 100644 docs/gitea-runner.md create mode 100755 gradlew create mode 100644 lib3270j/src/test/java/org/lib3270j/charset/EbcdicTranslatorTest.java create mode 100644 lib3270j/src/test/java/org/lib3270j/datastream/DataStreamProcessorTest.java create mode 100644 lib3270j/src/test/java/org/lib3270j/datastream/QueryReplyBuilderTest.java create mode 100644 lib3270j/src/test/java/org/lib3270j/ft/FTConfigTest.java create mode 100644 lib3270j/src/test/java/org/lib3270j/protocol/DS3270ConstantsTest.java create mode 100644 lib3270j/src/test/java/org/lib3270j/screen/ScreenBufferTest.java diff --git a/.gitea/issue_template/bug_report.yaml b/.gitea/issue_template/bug_report.yaml new file mode 100644 index 0000000..466eff0 --- /dev/null +++ b/.gitea/issue_template/bug_report.yaml @@ -0,0 +1,56 @@ +name: Bug Report +about: Create a report to help us fix an issue with j3270 +title: "[BUG] " +labels: + - bug +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report an issue! + - type: input + id: host_environment + attributes: + label: Host Environment + description: What mainframe / operating system was connected (e.g., z/OS, z/VM, CMS, TSO, MVS 3.8j, Hercules/TK4-)? + placeholder: "e.g. z/VM 7.2 CMS, Hercules TK4-" + validations: + required: false + - type: dropdown + id: terminal_model + attributes: + label: Terminal Model + description: Terminal model in use + options: + - 3279-2 (Color 24x80) + - 3279-3 (Color 32x80) + - 3279-4 (Color 43x80) + - 3279-5 (Color 27x132) + - 3278 (Monochrome) + validations: + required: true + - type: textarea + id: description + attributes: + label: Problem Description + description: A clear and concise description of what went wrong. + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. Connect to host '...' + 2. Open File Transfer dialog + 3. Start IND$FILE GET ... + 4. See error ... + validations: + required: true + - type: textarea + id: logs + attributes: + label: Logs / Output + description: Paste any console or runner logs here. + render: shell diff --git a/.gitea/issue_template/feature_request.yaml b/.gitea/issue_template/feature_request.yaml new file mode 100644 index 0000000..8aa3fb9 --- /dev/null +++ b/.gitea/issue_template/feature_request.yaml @@ -0,0 +1,25 @@ +name: Feature Request +about: Suggest an idea or x3270 feature for j3270 +title: "[FEATURE] " +labels: + - enhancement +body: + - type: textarea + id: feature_description + attributes: + label: Feature Description + description: Describe the feature or improvement you would like to see. + validations: + required: true + - type: textarea + id: x3270_alignment + attributes: + label: x3270 / Mainframe Protocol Details + description: How does x3270 or standard 3270 data stream handle this? + validations: + required: false + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: Any alternative solutions or features you've considered. diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..60d0703 --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,63 @@ +name: Build and Test j3270 + +on: + push: + branches: [ "master", "main", "develop" ] + pull_request: + branches: [ "master", "main" ] + workflow_dispatch: + +jobs: + build: + name: Build JAR & Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Java JDK + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Make scripts executable + run: | + chmod +x ./build_all.sh ./gradlew ./run.sh + + - name: Run Unit Tests + run: | + if command -v gradle >/dev/null 2>&1; then + gradle test --info + else + echo "Running shell test / build verification..." + ./build_all.sh + fi + + - name: Build Standalone JAR + run: | + ./build_all.sh + + - name: Upload j3270 Executable JAR Artifact + uses: actions/upload-artifact@v4 + with: + name: j3270-executable-jar + path: build/j3270.jar + if-no-files-found: error + + - name: Upload Test Reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-reports + path: | + lib3270j/build/reports/tests/ + j3270/build/reports/tests/ + if-no-files-found: ignore + + - name: Report Build Status + if: failure() + run: | + echo "### ❌ Build or Test Failed" >> $GITHUB_STEP_SUMMARY + echo "Please check the runner logs and test reports above for details." >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..60d0703 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,63 @@ +name: Build and Test j3270 + +on: + push: + branches: [ "master", "main", "develop" ] + pull_request: + branches: [ "master", "main" ] + workflow_dispatch: + +jobs: + build: + name: Build JAR & Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Java JDK + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Make scripts executable + run: | + chmod +x ./build_all.sh ./gradlew ./run.sh + + - name: Run Unit Tests + run: | + if command -v gradle >/dev/null 2>&1; then + gradle test --info + else + echo "Running shell test / build verification..." + ./build_all.sh + fi + + - name: Build Standalone JAR + run: | + ./build_all.sh + + - name: Upload j3270 Executable JAR Artifact + uses: actions/upload-artifact@v4 + with: + name: j3270-executable-jar + path: build/j3270.jar + if-no-files-found: error + + - name: Upload Test Reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-reports + path: | + lib3270j/build/reports/tests/ + j3270/build/reports/tests/ + if-no-files-found: ignore + + - name: Report Build Status + if: failure() + run: | + echo "### ❌ Build or Test Failed" >> $GITHUB_STEP_SUMMARY + echo "Please check the runner logs and test reports above for details." >> $GITHUB_STEP_SUMMARY diff --git a/bugs.txt b/bugs.txt index 58bd518..93d1972 100644 --- a/bugs.txt +++ b/bugs.txt @@ -1 +1,2 @@ -IND$FILE no longer works in CMS or TSO +# Fixed Bugs +- IND$FILE CMS and TSO: Fixed command formatting options handling (empty parenthesis removal for CMS binary/default modes and option spacing) and added Query Reply filtering for DFT/DDM mode. diff --git a/build.gradle b/build.gradle index 2cbf342..4cec3ba 100644 --- a/build.gradle +++ b/build.gradle @@ -1,39 +1,28 @@ -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:8.2.2' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22' - } -} - allprojects { group = 'org.j3270' version = '0.1.0' repositories { - google() mavenCentral() } } subprojects { - if (name != 'a3270') { - apply plugin: 'java' + apply plugin: 'java' - java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } + java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } - test { - useJUnitPlatform() - } - - dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' + test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" } } + + dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' + } } diff --git a/build/MANIFEST.MF b/build/MANIFEST.MF index 3cf97e5..4f9bf6b 100644 --- a/build/MANIFEST.MF +++ b/build/MANIFEST.MF @@ -1,2 +1,5 @@ +Manifest-Version: 1.0 Main-Class: org.pubvm.j3270.J3270App - +Implementation-Title: j3270 +Implementation-Version: 0.1.0 +Created-By: j3270 build_all.sh diff --git a/build/j3270/org/pubvm/j3270/J3270App$1.class b/build/j3270/org/pubvm/j3270/J3270App$1.class index 2aab021412b21db469c231efec7332761593bdb6..8ca98afbfd7d3548fa1daad88d0cae92998bace6 100644 GIT binary patch delta 17 ZcmX@Wet@0h)W2Q(7#J8_HgfD>1^_`q29E#$ delta 17 ZcmX@Wet@0h)W2Q(7#J8FH*)M?1^_`b28;jz diff --git a/build/j3270/org/pubvm/j3270/J3270App$2.class b/build/j3270/org/pubvm/j3270/J3270App$2.class index 554b90f85e303a0e311daa6ac37e7c893c8c3d61..5113d3fcd68b2b38fd2ca82a055035b4e5ed598b 100644 GIT binary patch delta 17 ZcmbOrF+qak)W2Q(7#J8_Hga(A002Ab1*!l5 delta 17 ZcmbOrF+qak)W2Q(7#J8FH*#?B002AM1*ZT2 diff --git a/build/j3270/org/pubvm/j3270/J3270App.class b/build/j3270/org/pubvm/j3270/J3270App.class index d7ec86044ac5bac4115cf15e4f51e6c0e4911ce0..e4eb9dfd36b637f1d474649dc69868e6aa813e55 100644 GIT binary patch delta 163 zcmX@v%y^`kk>k|AUHcdq7+f}Tk|AUHcdq7#ufpL!fllZ(}T8O5cc1_lO~jU4}206OUgXaE2J delta 17 YcmZqUYUAQK^>5cc1_lPljU4}206N+RWdHyG diff --git a/build/j3270/org/pubvm/j3270/config/Settings.class b/build/j3270/org/pubvm/j3270/config/Settings.class index 9c8e953d5526f702b40194d80fa7aab3409cdfcb..0614591c8b5cbae174358edfbe43aca39e8f0d41 100644 GIT binary patch delta 17 Zcmdntv%`nu)W2Q(7#J8_HgXuK0RTs92BZK0 delta 17 Zcmdntv%`nu)W2Q(7#J8FH*y%L0RTr_2B81| diff --git a/build/j3270/org/pubvm/j3270/ui/ConnectDialog.class b/build/j3270/org/pubvm/j3270/ui/ConnectDialog.class index 53ed31e7d1688f92798bc878b62cef8f68c40574..e8ba19b84576350a7c485f813e8b1bd7f28870f4 100644 GIT binary patch delta 17 ZcmZ3iyI7aw)W2Q(7#J8_Hge1s0{}tq2A2Q; delta 17 ZcmZ3iyI7aw)W2Q(7#J8FH*(At0{}tb29y8* diff --git a/build/j3270/org/pubvm/j3270/ui/SettingsDialog$1.class b/build/j3270/org/pubvm/j3270/ui/SettingsDialog$1.class index edfecf8971ac4dd2e3ec081862fc93da35b1d52d..145519e0393abf553f8601b369119d2375640a07 100644 GIT binary patch delta 209 zcmdnbv!93K)W2Q(7#J8_CUTfhtWYuL;9=lo;9_LpEKAH$$;?Yhtzcwe^yFkTf*i&f>+&z{bEJAOO-2rh%%#G*Ai90wy39W6%TA z`aqTukY)waQd&A&85p%>w=%G6NwREZ;MUUK%D^`{jMY`2QELkW`z{6v1_mI5ZwrGA ah;OumLCII=7=r|om`*T|!#(*FYbgNJ`6R9Y delta 210 zcmdnbv!93K)W2Q(7#J8FCvuoioT1Cb$-u;L6Cuqk%0{)lbp2is5^@nP!j`#fB;Afm!>)`)nz2j diff --git a/build/j3270/org/pubvm/j3270/ui/SettingsDialog$2.class b/build/j3270/org/pubvm/j3270/ui/SettingsDialog$2.class index d336d30153498df2714550060902505996469f84..7e3d3e9698d26b787b5985cf27102e379a2f12d4 100644 GIT binary patch delta 205 zcmeC;>*C`$^>5cc1_lO~i5%t=D^%Dxco;YtxF#-AlH}oG;ALQAWMC^x%uz{B;$&cA z;OAiwU=W;WtH1H`4;C+01~vu;0RfPDFbz}VEyN=ys~7&LY; bX!+_KV~|CX(%H#iz|0U16ycscm8}#2JZvPn delta 206 zcmeC;>*C`$^>5cc1_lPli5%t=m+5kGGB7c4@Gx*Pa80yTW8>jr;AP;On5o1sz{4QO zz{SYGR+gBflAN^h@(&g-pdtna0RfN_Fbz}yrdb)-fF?2lu^59DkhTW0jDR#Nke1TY z-patJrL&cRT}yW>1GknW%T@;d$)aqo*6dna7`S&aNH8z}8H`&PWI+6#3`$H42N*PV cFlhPe9Al70lG549V8F}}4iw>^Je92!05K>etpET3 diff --git a/build/j3270/org/pubvm/j3270/ui/SettingsDialog$3.class b/build/j3270/org/pubvm/j3270/ui/SettingsDialog$3.class index f7a00f1d3ff37a126525124d14a39a087b99674b..f5efe874ac53b801f14bb4ebb7805c43e4899c71 100644 GIT binary patch delta 17 ZcmbQqI+K;-)W2Q(7#J8_HgZg40suSL1{DAR delta 17 ZcmbQqI+K;-)W2Q(7#J8FH*!p50suS61`+@O diff --git a/build/j3270/org/pubvm/j3270/ui/SettingsDialog$4.class b/build/j3270/org/pubvm/j3270/ui/SettingsDialog$4.class index e9e7d8a9f010e35feabf69b76174ab5ec754e54b..3d9b553e552c820eede85ecf02c72b614e3373ff 100644 GIT binary patch delta 17 ZcmX@jb()Lg)W2Q(7#J8_HgY(#0sul31~>o! delta 17 ZcmX@jb()Lg)W2Q(7#J8FH*z?$0suk<1~mWx diff --git a/build/j3270/org/pubvm/j3270/ui/SettingsDialog.class b/build/j3270/org/pubvm/j3270/ui/SettingsDialog.class index c9bb75718bdff399999d0ad113993437ef781cf8..d925b0d3889a3041dc9737fdffb259fe8f21a936 100644 GIT binary patch delta 119 zcmeBL#@M!uk>k|AUHcdq7+f}TDCthVs3*m0&d4CIFFip7kP!Fcj-UFpfDdM1pf zlh5nPFd9$(pxOrbs?<) delta 181 zcmZo$#@Mxtk>k|AUHcdq7#ufpDCsgvPu{2}$!2KG$RMvVIZsY(a=b1VOwfpt!B}Im zm7LV%5;*~c1goJTBLh^Q0V6{YNQv=eZ9OX%OGXBb$s2`4CfDj2Fj`EWs3*f{4kB61 z7#WO!62g=B8?jAp)8v~hq_56u%E%C|F?pe?@MIq;)yZZ0HtZ(s%%>R{q$cOfi2|)- K-F#W!SQ!AzYc9?J diff --git a/build/j3270/org/pubvm/j3270/ui/StatusBar$1.class b/build/j3270/org/pubvm/j3270/ui/StatusBar$1.class index 25ecac03c86645ba36bb31b4919e2fe5843850ac..c4929f9d94af6c3bf58f197e58a8cf20f6a41ad0 100644 GIT binary patch delta 17 YcmZqRY~bWL^>5cc1_lO~jT{v$066~z)Bpeg delta 17 YcmZqRY~bWL^>5cc1_lPljT{v$066dk(EtDd diff --git a/build/j3270/org/pubvm/j3270/ui/StatusBar.class b/build/j3270/org/pubvm/j3270/ui/StatusBar.class index c7a9935c05b7d3c14832742a590815678b39e13f..e1574137ed7a7bda89d1f43513b1b519911a6749 100644 GIT binary patch delta 17 Zcmcbha6y6N)W2Q(7#J8_Hgb3f002c@27Ukl delta 17 Zcmcbha6y6N)W2Q(7#J8FH*$Cg002c!273Si diff --git a/build/j3270/org/pubvm/j3270/ui/TerminalPanel$1.class b/build/j3270/org/pubvm/j3270/ui/TerminalPanel$1.class index 7cb704de708c9713485d015cbfdedd10d6689f85..c0bbf6061a580ad937c213e3d7aeb034bf522b73 100644 GIT binary patch delta 17 ZcmZ22v|5Pc)W2Q(7#J8_HgYI)0sub91?&I- delta 17 ZcmZ22v|5Pc)W2Q(7#J8FH*zR*0sua_1?d0) diff --git a/build/j3270/org/pubvm/j3270/ui/TerminalPanel$2.class b/build/j3270/org/pubvm/j3270/ui/TerminalPanel$2.class index 371789c6aaf6e267e71eb6d715c36c093d5ad0fb..c26fa2c1e00e46b0164e61e62672cdc1778672e3 100644 GIT binary patch delta 17 ZcmdnUx{;OR)W2Q(7#J8_Hgc?F0sub;23-IE delta 17 ZcmdnUx{;OR)W2Q(7#J8FH*&0G0subv23i0B diff --git a/build/j3270/org/pubvm/j3270/ui/TerminalPanel.class b/build/j3270/org/pubvm/j3270/ui/TerminalPanel.class index 7cd527f3edd2baefcf25a4696675fce743202723..769c3cf665919dfe01dfcceff63802b7482479b4 100644 GIT binary patch delta 11015 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̩FoL9B-?I{EU~KZN38umB>}L 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 delta 9687 zcmai434ByV(yy+XWG0hNGBe2}Ol}egVG^!H0^|UOBtRs`kOUA#AS44sa13hQ&nckKZHk7?M$)qP^$xHIuN6$rWkP7-DqXkUT9+KJGdr3peVnS|t zJ>wjZs*KsfumT69DJ(t=D|A4*@R~7E8#iR=#AJynn96SXI+~WW`6^fYI(oVVm~zh? zIoB%oVYZeq3nUie8JgKuD2lYl zB8J5lT5&v1lu&?^j4{a7u5&s4B@#=OZdA9gqouN=$Jb>AD^8X;MWGR0zHVQSLOc?u z>3u9|YF^RUv_xSQ5-Sx(JxcD!84_ozXf4Z|x|f%Cw)iY~rT|>E(t_1IC@mbD1#7w0 z=3ClhK`-0)ceO5C&h2_emv<`Ht=K4WjxuX$Ue(pz*)?aS0_RCQOMzAc)I(?ZY>DTD z!PRV7vCoybP{Gj#yIyso@Wm3F!ss(Q3ydzCCANrC0g-AHyweqRsl;W2kViqS63@dG z+B?aUquQ~at=xu9CP~!BJ||x>q(e`xLrX z;yN{rvbDRaqubZjQ^RAWUR)sYLhZJc#Jt~2^y_-cpYk>zTLTlWm-q)!#nw2RyiHx4 zYi7Ju>qt%VZx9|cUM|3)D%F^#H9ccgJ~g#=@O0t^TyKT7c%{Uv)a*pF_H=&R(0V=( z_geg;#6Q7SPQoBQy{WymZJh--@o-eMHnnvwv*4e3;A=WNI`t4*qS%+eNW2cF3W%?$ zobJ&~Y4MSNm3RYg*3!~S95=FRZC!nhr&>{OmUxSHURrA8Z4z(CziC&e)w}PMco!^S ztL<#HwzI3fsm+4D99k9E$=Zgh>GPH5y%O)!Ea@5k2P8hI=rM|3#aY_X-MW;0e26u? z^>vMv<&BjUO5Xzb^>K;+R3>c$y-u64sJyPa zuHGuL@JWeJ;XW-nW4Irmk@&2d12u!5?q;4r)yMM^U*LKL)}=fx25c3D_@cy@6xqt; z3SWr*io{nHJ6f@WxEWs)U=QD>8e7|aT@J{ABUU_sZ>n|wmbNEjq#56l_%6PueU~w; zlHYtF@k5nq5#2paU7SxJhbK`58{v`e?6BfT_^Gn-88g+EI9Jt}FC>16Uup9)^M>QU zI2A(4H>0a*ExO8mek%DD7V*9%t>lhYmdp4-!Zcu$YSl z*QxT(wocCXgG7={6v5!q*0#2Ko?otw?6u*yiOiBLYG7?Vu=Tvad|h=*m$Lp@yvWEZ zDO$_Oaz(~SilsPha#lgCog8M0my|$G0cn~yD^0sGYjkW94Kb5TQnFgi$=U;1$s@QU zRm<6rD7cGKC8be1m%yb>0>m_BK2y?A%F>2pr)3S3G+bp(Tmx@_<&9N!wZWue+6YOx zG*X+F?T^fplux6y_p&dE93yEgjnjO?M*0gSouQjv#-%|}k)#PUQAONT)jgBTo!W$| zMxL$S6-z1!y*u02)yY{&Wt^2XnJ1fB#-T%14OJIP)Cf+MvN&tYOY9Duu-j~x`7!e*Q>mz zrG@vJZYz|~Y)&+aP`VA08r9@l)#PwJ(aYpPnk#9Z%2~;*~Vxu zQ(-TVbgl|JMh!`5ovUeCBx$iuT*4k6pe2%;l{r&)Pbcp+Ch|#Is!BiaV;wDRJ}(!4 z6D^n28V>R%VWJh1+Tc<4gqi0u)rdvC>5$Y3ExghV8*0ZC(eyh>UBL~!fj0_Oc(?>p zkEB(K?BIZ=wRJYHFkb5MvaL0e)@rxsBxRm2=>p|xOmpYTbp}T*P9C$1)R_HVdn>0> zT&ksym@BT-R*kqso0HoiZqT00%}hydYieK8(lm^FShdn{b6Q(#$BJR&CTPcVhl!iD z?2(iBs4#V8su-{To}vF1_Dvlrg!ZsIK@@6bdCA&G?q~cr#zoK$73EGz50R2~DQUN) zzw6SYO4=jo0ea8|h5Y()1^iRe{ZMG3CpoQ*Bc8C(K3*Nu`T2}m!uknTNqq7q?{vYM zFDT9ZlCIM2yriU;{gSTM#aEShK+=7>^tzJXkaVvuy``kLCEcS-?<(m%Nxi!Cfs#Iy zbhj>jtfWsQ-K9&PDd}@bck0rYO8QFD9lG={C4DXZ^fz7nR*Bz9`lBxWprjuq{YjVp zqokiCU874sE9ndX`9F7h)nIVF>X_qfOPH0(dn8TJKm~xCjL}Hd{vt@Hr=hZ4i+6S z1*YmS@8bHCw`cvyTdV$jRMW;zsp9&kB$xxm{B)@!gZ{il$6a_|@X33&j_(RSc{gRb z3{VwnUdw(hW!w~Tv3Ab56z6(=6A9uNe-rTg=-~US^!Lx7^qxPdpitL8I_^%^zpbF% z{D&ZtP?*C=Nny6|Yl{j?hF)yg<=@xI=!1q4pKt{9!B%CXZ+GE81goqX-z(N@X+ALX+q-PSlqA(* zE<`232}p+sGnklYrc1PUC%DX)(FP-g2@f*+-ihVh`?nKI&Ag8&{Eo4clvm@5XE465 zc&z#Ip!KcAKQMlK$r{F8rP<~yf_Qo91ja8Y&DML>{Yo|C20-zEcu?=Dzw{DjOe!m6 z&GxdCtc_;c$RA%YaOQr+^>AfFBj`pD^Gj1^gTa{Gx!vVZadu9Mb{65zVg(IWb5< zU;r@;AO&!$ga+Vw1w@7c`xRgw08B*|r^Pnp4jAUN6h-AbExXVHY}FaH3!?@|ms)0t z-V^WU$D>rru>&{*aSE`90S*Nu3;^x~ObVduVa`D$mB(+ucx{8H$?p|EiiP~W-^Sne z7m1(6pTsZXR{ombDh`Xs#S!r;f5m?)j)|YduNWbYW1={LBM@*R3amoJvypHGn(#t2 z;~&w2x1bg8!DxH{B|gFJXE6@nK^uOJc07g&MCjDlN;Ozcz~$gKtA!xC&0+&5U@!*{ z!SG|8s;*-Y&1>W^3_T(4fvDdQdFkkXRmvWL5q!etPhpkV9NL4Nzr@cac>zqbi%qw| z9ByTnrP?xYhci=c{#&6UH6DlE3T1ir01n>*Mf^T@3yk4Mo|aRd;~)L7J8uV$8yv3D z5X~c(%wLEp5QnLdjcG6e(_t2W{5N1G%)_B@4raju%z-8xp@$d&;wTq6RQEtZ*RsIG z&H-3=@O2AA5q<=lG(4B?xBESv!&`9@$Ao*4|8vLcDC@sUZl_HD%oF?Bf`hckf!f+nk!hd zjWyd@volla~`u^~1*=MP*YU8jRJUnm@#JS`zl_}*?9#ou#KG8SMnmeTK6Ug=mewy7fX|whez-a*IqzZ5a80& zE{DBY=RI>JH$=)p`5C zmG?MavKg!##AUq@&5tXpdryQd%q-w~jn{J}x&fkaGsO6D3-@&+uald&zgsw(+w}gd zK*zY6;Rd^zyWuOP;J96_0^qP}-8*q(Kb)}>uda6Q7FqN2h==fu9U`w^@R*8W0g)Sz z*XS!GGtX#kfkd4ekJpCC_<@@Ls6G`=JIOp!J%) z+UZwQxJ^F;04$@@6r1gDFoYc_OG(*=T1vrAyuRU5!Q0SR?s(j?4R6|sxAx;5EZn^f z@6m<(S@`=l+^P$GECjaUPF>i|!k%sTm=a*Vo6XJ-z`27ix`Dr-0UrVjZiOU%rtw{m z8~eD{ZifoK)R~O|Scp4dDelwxqK2L@F||leUOMxLozxK8zR(1J;06divA+|$2YBts=ZO;_T$t2_?(*)Xn#MxtU@=)MrnPO z`#Zo+zQzu|&V9WBdH5!$1z#}+`zqzRQGKPlcjJNic69JI|2n`9e4`)Vc5lbG1Ni=+ z)mXI?;yav(AF#y_*~SEo{t`6B>sHUWNOxX8(4dl4q4)`h|0y%Cqw5u43)JQyex!X+ zn;MCqY~76?{kbmP&n@=FI8qo9e98Is6))j$xX*9-d*C}L!5;?}S_#hu-zORUM@;2} zba4eqQaj=2Ja+Unm>$l13=h`iZ^N&*;g13QFSibF!(*zYrwg9QC*qau;(zmGfY2SF z$jST@rQKVXVP?b@phWGZx^w&~MNzKUVrxjx8RxXI4JZGbs}!BlPNrvU7Wd_EqwD}% zK8evQHaRVud8l2nE~_&tKsm*By&WKTv7;C4d^`)#Xa+fviyf}m?NqQCqV*>i3A@X> zk0mRk+IDY&fBa^MG!?}=qwH|v_d99QW|;c_`Uao;FW=x9r*kvJx$MPBE=RH5l~nA= zcR5ZI1-k=x79}|~-|S?6X)l;n5h)&$?-~-IDfuow0&(C;JE`1=9-mtrpc&4D%`kT0 zr7>=~i#fesZgIw`Fy=1C4-RC|6i%w1}fIB8A3^Tq+P_iT~3JO%QYW66ze9B$}yMETt0B zPNiZcm5DWc{dPXzv2CDov5}^W>u83!nP!Ry`5Mi?lg<=-X_k1EW{WqeUc5^U;zMc_ zpVC?4Ak7ztX#t;O&&4=eh{I_S=FwstM@?8xKAcHQv6hzMDr&=vsU0t;4*V6e+T40X1I%M4R2Tcv9l2pLj*0pF~_8sT3O??##2;S=Lkgb-=omQ#euImgB9mB zE=T-P1RL|V(^;D#Rz1{W=;fT*2hMy0RS)ez<0#1o#*8#$v5eQ`oD{W!B&SGOL_se^ zsfM{=u>R=fo-P3gT?%=08DE)g;B>eG>S-f%&{cY@PKa_ir}D>Kn#ia$LkYy|zo_nC zgFfEP)ld0&E<3l-@Nr?#$GD)A3T7u4YW;IOBQIvIzG3i1kb}=bZa#!e(p}t*=$=+xlDhegnQ^Fmk=h z^W9K1-xg)j?Rum!5Rq1{BDCwjf;OuyWTt*Zn;6`$cqQZ8`}Drv4p38Q#ere)E->@` zSuFJql@e?wjc<(-@O_<{1X#_vq94-qoViIqya`t43#?%L z=^Ve>$Rgd50csiCeT?rQJ9GK!>lAK87?+PK2HD1BzBW6BYziX}aO0TE7j9u(b?Az8 zrvzx($xcY-@;zXXt4@i^VYAz2(aGF@*hVi-Fq z2k5+$91*`Vo9`}9V*3@n?lkmcOy`Twljw?Fa~k$hX7i=#Y1p$)!#=`nzHn7+byN)Z zyy`Uc!%XMf+Mw~lIp?WqKRL=n%;kIDQ@C^*?k~*s^Ih|)2M-q#&ra$d)Y5ZW%;Nu*qZ;Bw4QunIcTiiM^;E`0xC4G#J*cB+qNrdl zUzeZCrFAD$r+|gF4g?_fsm}+_r|Wk^$$Z-^TZbJUvbF3WZy#mbY>zTX>J1U?pbH!3 zo0Qk=4|dqDLuLip3hb~y$g$|aR6BfRhnIDVZBXrm z&1*oq8k;u$8ko{t7IMcHr5j4Ol@4@evnoW_xXW7$4?6oU&CCzz>R{ZQ+pQ0voB>x)q9 zV5s#?sP%oQ_3u#YzoFJ4Ze3vbbuRDZ5C9z3S{A5$uRV@fwgnKDd6P1)f3KNKc4y#N3J diff --git a/build/lib3270j/org/lib3270j/ConnectionConfig.class b/build/lib3270j/org/lib3270j/ConnectionConfig.class index 0cc933d760393d8dcfe73a79f0eb5056e7fdd817..c724b820cca4b0461e36dc6792dabb73a37e6897 100644 GIT binary patch delta 17 ZcmdlhyjPgx)W2Q(7#J8_Hgas`1OP%x2BZK0 delta 17 ZcmdlhyjPgx)W2Q(7#J8FH*##{1OP%i2B81| diff --git a/build/lib3270j/org/lib3270j/ConnectionState.class b/build/lib3270j/org/lib3270j/ConnectionState.class index 73e6adb3d00bfdbb53f1da2d2180c48a1f4b6db1..6b99c5dae722c1cd14f2bd92ed15dd428dd3180c 100644 GIT binary patch delta 17 ZcmX>rd{&s_)W2Q(7#J8_HgX*01OP;82Gjrm delta 17 ZcmX>rd{&s_)W2Q(7#J8FH*y^11OP-^2GIZj diff --git a/build/lib3270j/org/lib3270j/DiagnosticClient$1.class b/build/lib3270j/org/lib3270j/DiagnosticClient$1.class index 47eb1aae30e75796393e12276c4bd505d93b0f0b..f97f10235ee85b7e2159440a9ce58316ac809073 100644 GIT binary patch delta 17 ZcmbQvGo6Ry)W2Q(7#J8_HgfQ@0RTH`1)%@{ delta 17 ZcmbQvGo6Ry)W2Q(7#J8FH*)Z^0RTH%1)cx^ diff --git a/build/lib3270j/org/lib3270j/DiagnosticClient$2.class b/build/lib3270j/org/lib3270j/DiagnosticClient$2.class index 495f49bc2e1952ba4c1b402719f09ea9eda351f7..8ba4e60eeaa2863c559b5ac13f65c40e2ba3e27e 100644 GIT binary patch delta 17 ZcmX@ZbB2fG)W2Q(7#J8_HgdSI0RTdC20#D+ delta 17 ZcmX@ZbB2fG)W2Q(7#J8FH*&bJ0RTc|20Z`( diff --git a/build/lib3270j/org/lib3270j/DiagnosticClient.class b/build/lib3270j/org/lib3270j/DiagnosticClient.class index 86dc20e3669b050a6e4878e3835fc61f87c16fce..f4c643d0b5ba6917bbb25136ae7fc9ad61ef32ea 100644 GIT binary patch delta 17 Zcmdm?wL^>J)W2Q(7#J8_HgXt<0suo^22TJ0 delta 17 Zcmdm?wL^>J)W2Q(7#J8FH*y$=0suo#2220| diff --git a/build/lib3270j/org/lib3270j/Telnet3270Client.class b/build/lib3270j/org/lib3270j/Telnet3270Client.class index 8ada1316dd4b77a334def7f87134d67ae2536b5c..b4871f203125d0f040f1f13f131db7b4e721aba2 100644 GIT binary patch delta 1670 zcmZ{kdu&r>7{;Hs+v#af&(`krMh7Hih)yySh(VVnCPWQ{L?UWJV)%z@56fV5*tHS< z(CS|rF%dbS{;4s+#Gnp`4I-J`M4VhFcpG3Sl|!8%gtroyh$Fjy!qi-4W_hs%Ok~58~8T(nCqxl zi99W*ytOSi%BVNclHtAMToXOb<87pWhTn(buadVp9dy?@Q=JOk21u0hBY3oGZgJYv zrzk5FSO$d{@Wtl~h4py63Y~LWQwp-+;S%Xxnt818L&MJrebY4}oF?%~A1H2K`(LO$H!e*`tP4lYI0MWT+Y}DyI-7oMZ zSG{o8K&w`{Na`zmtynJ{OLWvT*HZfi-%e1)91ryFh(+h9h#5Q&bs|B&P}kHlp4z6R zJj5#BVO2F{Q>>G1I2=n(tB`7fZww(5--}#CD}iTg*v5G4V)ZVyqh8O$R4q7HYbEuo z8K};{Qjw7d%6Pt>S=+M#K|D$3M{MCbI@le*P8hs7u}~`B=|sEvQ_b+~-(0e(VEa`3OD&vLv3)TKqJ6Uh*<$6~(k@Sl?y^HiKlhH3} zw2dL+uhwvH$&^&Q-pAc^UK@8F{ExGj&W>?s>y*?Lf6e`L-k2=>buAnRNDoa$@6zdm zq&ecaYKK+=&*4oSA127r_kUc`dv*E{IS$5&>9=(H2ssYSak`|nxbI!Tv-gobtV>2sf$nKH) zMgVgetChobp;AF^VQ!I(CN!(jqQ^yErHc=~q(>Vr^Df;kcGbC3pTpP#-Vjn0*~cI` zvgS=$VU+MnMk-$GF&}*zyyY2&7{hxR{TNW=gB~C8$+^K0#EkeN<0}T$_@>7yeCOrH zZWVs=Y||nMzZsHvoN=O|FmZ8yeqQll2hR*W#N50;bQbI2 zN#-1SE;CQIbkbIWh`&NtDS|!9W^*dB1Li1m7qJt-ECiQfr-zKPIMUq&3n`EVA(5c6 z&S3;pV7Cwvlmr$10qxW?n4aQ7c`dy#xcL@~jS4tuiC-vO3}g@FLW9*XVGSv+;kwsi zBHcv}i7aAjlyF*=#A}JVfo>t)7Zz^yRg_QAejE@4PYJW<`IrL2ZsL|;$j0A2EzrG~ zM7MG0KT=1abQ6j0;+~N5>M@mUCae;NJxotnq+gBO2)mCw52Llt-YRM|(-?s5uwe&c z>7Gm-7rhOEY6LnpdKeo*B!wJR0*UhPaT@jbh_}Z?$)4s{P+!GBdX>Qvo=mvtsZ2dh zGnKe-oMOZ&5hW-=d!WT$LLQ=u)(&T{bsD4NAlnwOF23C6P(M-#UTVuAywjGjMARum zik2j4SWKDW@i-aRDH%!OOX#A6=Lk8W#PIY8d$wNHzJ0gT?4FF~P9&Z9>CGA!F4UpbiDiNL676&^>5cc1_lO~jT~%z06ZTBt^fc4 delta 17 YcmeB^>676&^>5cc1_lPljT~%z06Y){s{jB1 diff --git a/build/lib3270j/org/lib3270j/charset/EbcdicTranslator.class b/build/lib3270j/org/lib3270j/charset/EbcdicTranslator.class index e21435338ed7b6e5461d4b1bf7e63bcd65db9e5f..2469a256fa6115f71134d38f9fe9ad404c3659aa 100644 GIT binary patch delta 17 ZcmX>saae-m)W2Q(7#J8_HgZ_=002U-215V< delta 17 ZcmX>saae-m)W2Q(7#J8FH*#3>002Uu20#D+ diff --git a/build/lib3270j/org/lib3270j/datastream/DataStreamProcessor$OutputSender.class b/build/lib3270j/org/lib3270j/datastream/DataStreamProcessor$OutputSender.class index b5b120b77aeb595a9f68854c8f3cf535b951a710..cfce635d94f9a3c31641d3fd26fb53575b821853 100644 GIT binary patch delta 17 Zcmey)^qq<0)W2Q(7#J8_Hge=M0suz%2B-i4 delta 17 Zcmey)^qq<0)W2Q(7#J8FH*(}N0suzo2BiQ1 diff --git a/build/lib3270j/org/lib3270j/datastream/DataStreamProcessor.class b/build/lib3270j/org/lib3270j/datastream/DataStreamProcessor.class index da1d3aab164847d8971178cf42f1021d83e0394c..f66aa83f4a2c845e936bf11af4f29c35cfadfe21 100644 GIT binary patch delta 1608 zcmZuwdr(wW82^2H*4<@UK$doSjJQ`Ib>X78uz(%`|(c_#jhDTt!4BwbHWEo~FGt`=@_unwq8=hTOh$*tBuHcjkBQ`Ofcq{J!(u zZ7mnu?B{Rv4*)2qpL^_?&dGM9&_)Lt^b#R{QTx)S##!ybmX7M+@}{N1RzlSHxzj2t zD!g;2IT1_GL{lDZcF-1j#RxdHs8{JVE4}WZH;g6DG|P4e?VzsE^UfBN=NKU=5C~{x zmE}bmY2$NNl9$&-9c{r@8yz<^mqsUzQrBpD*I3}nZ8%LxsF_wOsV&P^l#urDvZfO9 z+UPxkwPuF4cwq_I=mXwVlxYjwG~S#e#PG?|MrJn!o8|dOgpsqGA8B5;qFGZ%oZChp z8^61B`qW5ur_)8F)SW_~8}r<0^rg}1PA#_4*MyAf;A8E<*0x}y#%oK5*1`?w(8fn9 z_7>SSjp8iyjh!yj6)Sz~z#RI{IN-icSB)1EGwCPecw&QfCjb-eGCWBWN9+dC9-}6y zoVtxIN#4#kX|J>}BMl~g0ZV4q5$HYqMbyn`SJncIKp|V+qJ4Mb=iJUOX8sgr?xzEH z;^#&9NjDQrWrus1`Zm2I9gMo<)Z|;~%Mgunwl`p*JPkdU>=M)++BGVX=%8^THHUhP zUsL_@J;aYH{SelvIJu+$WU1pkOPmMUo(@|WV_~x*#>y_+Jsu8r%F|}$VE%DCMV2I; zLBJA5HKK-DG9*7htSv4f3?G~kZ;`c|3*DP_i4#1b*K#43^-*m3OMieZrP=8zGFmjm zKX4LhgRIbmm9VM0R%drHY0QMOyCumD^9?*mToVmJw_?X29K-xoSgsU5Jz);thjDG< z+DhpVKvZlP^I_pyxH#zu48mg?;;SA)7KgZjfU<{CbURHJvUa#Zp@TW=WMIGVSr%~G zr_-L$Z@#P2bRlnFg3L7&Be@@AZgy?GUvsKPTd_%I;6*lbz@N&pD%Nzyf5 z>Y6yrAs;Ks;PO_{<5ChI{{bsE1u!xX!;@wF;`emESa}?q>W^Y`mB=@{%=$nV3bPZa z(R>o-dZD|_x$y|E?!$dGeW>b1sMcjRFBqvU-->9aJt-~g%+wt|cL)J}AiEd)duee$ zZ322x$7%Glh>?qajMkeV$Izt4JRdQD$eo2mk%u&qi?PCw2_hebq5!i+A?Az8Si<{9 zMKRWhDR@ehV568yH)T$W$n-~K25?c`w5g!dCnq@t7S2!(hnNnxn1Kv&FTA1x#o~Sy zM1Z_p%=E~BtSKn}pIaqjMHO7)A*73$$QIS`iJJf1viUk&BW`~e6m`BV&>g;VOPdpB z&O8UHq85`x9r8rIa*YO$i``24AiS_qXgCCyngRI}0S;$OFKz9kZ6O41{oxjMdKb)e zm^mgoLcP42S?mTn%3tmC5J#7I+o!N!;R%HU3Qs9Kqwsx&XBB>^@VvrL6kbsHnZhp= ZensDN3MZ%Cz?1)jC5nEeYjhpS{{rGaXQ==H delta 1240 zcmZ8fTWnNS6kYqC>Af?eU@K5Y3ZfRpH$Eypz^aJ}i7_Vr7=N&-9rrn{54<01=B~BZ+IydS z#{2qvz2~M+j{#UoKa6;*Z0{VZqo;i&=^6Qe?Ra+4vlcz)qvz!=dx5#fM|38Uq?D0qFw0zQEd;6P&lJ%=rI^-Wz12mHr-K-Ywbtv)!Arj+Po!;RiArh|@>1Jy1dC zWKW=!K9$=73+QusBv4j7*Q77Jbe_Jn=qn$(=xb>PFVVO1mtd9aJ3>VM8jQ$vsL6Vf z_cf?rJ`}1c*axBkIUH)GLHS!KX6>h!G*cc9h6lOM3xn}G6ri5@1N5?1Z@NNlWVMO; zgLFu%k6)oKXLSYhhv^lqj)cp&*LXNc1G%5WenEq|Kj#ODhNM5Tgob5XBwajA{BU2Q zSFu&Qo{0R;uE(PH+C$7&z@5jna9b#}*z!j-%3)!r&*HEh3-^m^Rdf~^Gmo3$nW<8d z`~XGnxg~j|V9&a5t3JR-V8kzQgNgX9JV-JzpP6-uwan~JR4_A^2r+XpG04ori(9Ty z`2o15(M~)7O(AY!U=qGbUe>^7^r)e8mlk&s8r=I`Q+41U)t?8rjGD^`8Kz}gSMwY_aO(q4F74;I^Gh2$Ra~*!wPwagDc-V@ zOE=Ras+8q&kiI7cRil(0VS-0t)R#xp!6njMp24_Ij@Ngjlv)ZC5DoB&Mg&9?Dn&D5 zVmTVc3Q|1 zC)8Yqp38=d95Vl| zX=?Ql)wC_?E^q8sE|QuEzH@eyH(d bjh|@zjK1M;n;UND`*gwd&_$Y{?-BkBm=F=c diff --git a/build/lib3270j/org/lib3270j/datastream/QueryReplyBuilder.class b/build/lib3270j/org/lib3270j/datastream/QueryReplyBuilder.class index cbe03a760f0bab80bde82db8cc2caed4ba6f3244..53f50e6d30b11b99bb64ae6209a8b019527e64aa 100644 GIT binary patch delta 2170 zcma)7dr(wW82_DJcJJ678Rc;_!?13Mic}z5fxWhaAgr>7X>vI-{Yk*?2hw0vYu_L{QSOmA)aBQtBvnV{dj%LGmR5yLrmf8Y7O?|kpGI__+O ze6!_zGk^)$c|x9~_sht&U>bS}be>=9_LPqEc&635YU=Y`)gHIY8wr7Y3%Lsfy7bM; zO3Gg7T;fdeI2Vf6R<0nLhQ;Jy|vd|lvq%z(&TcNZ;G=nuWG4RMj6T_ zEU>_hh00z1aHGe9N-R@mj5@!3^X6}8n8wkx}h zWBpaAW(F2BVLH?x2Hp~!>LO`J@;iq4(inLD?83W z??^rk^9dq>D#dS}$o-3mLOg;Z!%l<$4 zRDFJRoA@6BLmaAOjgA1i2GH#Q4r+L1TL|Jb+IEe$#?d2y-Wp-GMp#RsL=Ag2ypEXv zR8peh^#uJjobT{!;Ui(7Mi{R#>~Pp35WoF#A!+(wdD}%t0R%V=U(3adzvy2+@cQ_iX?bMDk?=9szfHLMHXtr40uH$YDF2A2si3PJ?g~@EEgNG zLTpBZXo6qt#wu|L#o`#&i_6%623|i15#|x5R^DxvAWP&DLV{KM0sM(8LRdRRSos%e z3;zS54`HDBJ;<+?aD{2qM#Ybc$ zcZyoaXD~DSBnp-LrlbtDUApiVU?)u2#had^l3i@CCe7lm(6usL2F+X=x{@)%Ly(M~ zZJjm8e8sP-nMFmw^X#A(V8b5r?4_!G7=RZs5-(vQUdCh`;JiM_JRIT#JF_G*a14GN$2y##S10MkYt(WIM{t_AB`{e*Un-$T2yYtgI0bV6bNI3GJAc7w)j5eR zI<>!EX$+56Zph*NRm*~rI)@0HXG|^-U!;StV+fit0+*;gDHth5AG%lbrHp*Q8+-%a zq%T+L*)?>>b$WaQiFgbBaFa298>x5)qwp@q;TDVNJE8$R??aZ?M<~I^Sb$GB z>u6QaQw*o?;ry|Y3T}st_6uKDIp$Fu?{J1j$O962dPSXG0VWuRCc z`*i+SDtf>K{f2z}PN~6dQbl1sqf{xQHkuysWLPi_%GR*CY3hlCnt&GWt-KWI%=)kk z22|)VjJ-5Q7%*NKF^P4qW3bb-O70uJ%&AI`p$!nBkcE}f5tOzeMnob`bV5A4FiCVl zis*`=?8bBvgG|v4Iifp?L=Sdq9DP~E6)cMdT2TPmk#Yd8v#3z?){F^-?foLt0Pw<4 jI;ZCwpiYK0%!K-~=?6J_#9P@#I}rUBVg{eV delta 1332 zcmYk6Yfw~m7{-5xU3THHuq>I5RtO{tgc?YB$w){|5@NXsE(8d&2#RrA6fgq>wY+rk zG+n4Ewfaz2MwTF&)A*r_-KKG-tftF!nZEY1>4Wv0T_*Xk|NnW+ZSBnk?d@f~!S4R@U}t+NZ z$`7R&DNX&MU{6X%OG=krn9?1o^tu=_8D^iG5f5t_wQ^inoCgiktvXk_!_8yX?e2R9 z+t`l54#|(Aoq6n>5kI83V@BMmxNAn-O%m}WsCJ%hi}rY7x#`gwQj{gkDD7s}1dgoR z-Y0fkkwLH41RPoyCgEfev1I7=F4FA6V=?E9UTZ35@tpe^p@d_UauUCgZXwxDu*Tw6 zZ!EnIm8_PCfJ+o^pjv60SwMhHYRI8hg>ywPLMf!krR=5W@dhR+FMgK_E7g-(XmgrP zwAOlJXi%pu0%?$jCU)!X9HRb}+-NtZ8?T!_!+^i-*Q7>xTSq*X25p!c0YM9KI$9sC z%4rkSc9zKIyJYe@nY>ZPmeDDuU252^?2x|n>S-5!YB;F%0R0?gKnCpLI1h7*z3P_E z!f7o#Wua4@?#_CT*t1k?b83Ik`Z_*uu}vgm%rJ@UlLaG+N2O;!%LKiGg96Kp7-Quo zoCs5IQ63!Dq#qIWqZ0BsH}ix9KB;4Rifoa|!QxJyW;M^~$~?<`JV!ClOU{_&yg(f< zYP?^P@Rud@xP+aMkdqAYih92)^w-om`~M>ui^#Cby$|?p)=Oj@yeVKL-&V)}Vo~Uaq(!m(UKLU9nxFylpu)XqpZcCImXEyL^tD zn3DMO0=l5{_<#y7sw#K7>QG|pjdqnwPQxXAxg+Ab^m8n~kBZ}oIl)!(V}_sr)9P0Ei#`AG_X zCXZiO#jmXAH(i3?HOqg{%%9TIuU}-MTf;K|#YyH_8YT&zVO3)61vB9AF;p6G`WP+^ zMEe*m4Y+*lxBN*9@++=i9#ixXi8v8(XxQxkfjt^F!VL!1WUo$5cUVscc$7oT{SWml B^S=N9 diff --git a/build/lib3270j/org/lib3270j/input/InputProcessor.class b/build/lib3270j/org/lib3270j/input/InputProcessor.class index 6ec5520e5755762ba09cb2dc71fe61a5440329f6..70a743705a7c711f0136f97cbd35b837fd6e42ad 100644 GIT binary patch delta 3261 zcmZuz3v^V~72W5}H}gqCzLU&MOpqW+NFaPQfj}Ukg%Sh|Al3pS8Au=o2pxh8+(4 zURyWR`kt|1*D`0)xKe**z+XFfMxdtF7pSeV!Gn(-_yd*-Y&HHs*=Yad8P&S-9tS?r zlugzC8h@>3?o$W$p-y1Y1Pd!DbpQt~IOM=*>SjyokOTPKfg|{nz&yF!S6x^uFm@T5 zF<8_6ivwTauL5W7+$#TIuKEbFS`!$YG1i8oIA+0D4jjh`HNo23{x^JW!6^q$(l0M9XNx3G;X&p6!}5Tv*oGtY-xRMy5Briv zU-fkE;;XF<5LV6Hn$iB5jcaTcG0>j6!JcnV19kCFKF_L`>{%J-7~*7@`SidD7qNR0 zg%rf$AtWJ{a{t1=b?IWqpgK)D0VY0kDANrck&6uUKsI{eJZDWXq!?@z{|~;?lsMH9 z3jcsPGph-G8~7&B%-HnV0;&w)%ms9AmG^QKuA4HqB70-IxLKQYppe7M7|7>YHOARJ zCk$vXtSEvL#e_bD;D+je43HEG|2Je_t#T$5YAU)l4CBi8A!D3NX$R_VLSYzaNcJa` zWlFV;8}~bBiyn$qc!Y|hxbqW;Mk&KJ4w-n80eK3K;-?sd2^fxvobh2i{Fsa>s6aWY zFda3h#1i}*ZzF(}n2C*?*@W3>z#MGDv)F^VIEi_pA+*DnaXmS zhA*Uw#q%=0lw~+7D{)-b;)HyJld=^%Wd}~nE_@?L@U0xj8M(mmGS10uGWERL661=y zDC9~D+2F9iCKfES;G&up+et2}BV&`~tU4{WN0vr|7*ZbOs*`MW5p^mq>Z~zBaakC$ zOU$gswEeMza>Go!%Gqnwy^b!pf$q4e>yReR=%R7=a;sKe+^GAs?r^r5T6fVG_sIE{ zuvTA9YfG!v&bX|!c5I@sCmGOz(M%E(rLMF~xpSxXV~Iq$gP0#CtL~9lsTDyj8@24z z(hzHYl*ac7`z?_SP82+3QzwaS^V_5O%{HpbH^paoH7t=Zuxt|$C5cEB7uT+}O5BN` zJzSH>ft?Z_?(2n%C6^`-;bBQbSLuj6c^HpNCrprZOq4DtlMH>g2q^a`B$7{q*INzl zO1Pp^bPBrv{n$qG$vs>aa8Iy z*xt1K-VBK|4@LYU`H`q~&*d;SXcP$|7#d#Ch@$B2H)OGj37bia)ku(o4{QU)Et)6cDTPxkEWzz|XZKJ-JKZXXr8Ru2&z?JJ1R|$D3a^yAilGj-f zZ}3|4CWf-Vm5GXSVW;MU4M1gCJ9M zkoot4%&Hp}if~DJACucSDc)4o)<8xEQMw$?*2(&o?{vrhUrL6c1F^hA*Ot?@6?E;_ zbZsSFTSeF2qid__+8VmHmaeVCEcpQQQVPLd-%Juo)xh{_csov2mEbjb4jGT z&23Ke*kmhaWO@-$;y!fKh9KPgkly+hMA~w=OgpXP4NB>FWAxS1;o3@85WB)_cFypg$md!0W|HT=?zj^s z9!oaa&9!@>16R#WPOjUVNxo!}|;O2Jz=#C{o{YD(q`Vf`c5T>0?aLSLmJ~C)i|8 z!Yg01i5_L^I<5QegrOM<`%bdl;$@6g!YdQEZ@is6D0I77gfyDEfC%}X4d(~8>Pw8? zW%Q6MD3Gfd#`lqOoz-^(I5EB^qay4!Equ;F=OEP~yantx8x`up6UlZIaZ#9OErLMiziuc ziuw&w=>)5iPWT4Blvydfz_ulkOxWC>MExG8WRSY1_I!7usP=Qj*n;?SJ5Dy@n{cW^ nH@{dI#7r4QEIe4XyQ2bkKgKbFA2Iys!eHA5CZQikyE)>2cle>Y delta 2233 zcmY+F33OG}6^6h4Uh-aY^AchbLuQhIl7Xaws3?Iz0$6c@A}WJbN+b~(suW`Cgh*Q~ zbWvUDYuD-oBB)gAK*%5slguiJb*PCbYHX#&Vh2Dhl%fBlV~B8ZkxVibp#*>+=3*&qH@ z+?5G$a4^W5A^yNy;X`q^2i^(sE`Mwt9>2uw)9^NDSd721?#}Qjr+*-JLBqm^#@2}m zLGud}zAaRhkd0XKo}OLd)uGbjZpD#BK+kMq=qo-s^dXlt^2ns0aPM=-L!S)QE_TuB z#Ohh5{c;MapqRn>Rmlgk#^TTOJA!}6Vb@8fLVWs%KuKvkLtFG_xW_o2b%b21W%-E9 zd-1tiP5ZGWZInHK5gu@jAx3Fg>OH%{I}$6(ql9XSV>F435%sYmcb!MbkLAhbE)KqW0ud4tdwPTH7l%|$89`M*sV0%9sJbl3G+d? zIk_w-X?nc(B!_$+@AtS;ynjg&zb2L6kija2|0I{OnnA45?plWNG*zr)0?%<5>&4)A zJSvt;*dX35JWi{2+t^4uTg7;vB;U?ZcxEgTT%H*6LMZ4PhQeBQRDykpJ0Yb)upHFVo^yl-u6woVS)Rz9|a zd}7`7*fFig`OMC6MDf0unvvFH(#i!XAr!>1Ak9H~!iQ1^SWoyuYNmCCGt-8Yx-v+S z^b-s(hw0YJ7GPl|6XY zaJ5LE><%7|iq(rqA;(#qyyE5McH@y=FMs+~(YRF|$CAjgWCmEu*Ky~#asQvBoLueQCEKB{J4>np@xdlBo?o5s6{auS5d*1b7osMZB)yZ)BXAS+;)6>_8XXSxmG z+cuDTEAnoO$9G;Jvnm~UwD-VyeNT82y-U*vY1^?uq*xh+R!*_BQ)v}kV}lu~-(#(k z@pc6h>?&@xp){xu?y+ll(1xogMyLm>`Gt*Sm5pMZG`HTy&}n1YZr5^1`s=ZA9+6A} z=amDKgz`@l41bi7CXt-TIN~vxq=AAuD?w^awHuVM8&&5?l-SKwsoTcdWNxu3%&@7< zvuWIG-%@AZ&PpkIwM}QO&EN&AW24iE1_3Gk21Fp*D#O_xZZcZJ}cSh@8?6 ze!qr-(wq*qRp%xwTi;#}r%7G<%Ucdx<%=f%|MDk65c_Xq&Xs&d)VlR@r8LYg=fs?X=k| zY_c7iQ9Ct%c4@}!=D6+QOWUg=?K8*rCs~pmuz_~a%I!^!pKlhF$MH{AxcLb$D#*%o ztLJ^{tV_+&E&IPT(%sz+1?h9$UGMv1A1WR0V2R{)fL8Y}l;U)-cM}I%c{^H}$QLO^ kO?grnp9aw>tpU9a*GT$G?q diff --git a/build/lib3270j/org/lib3270j/listener/ScreenUpdateListener.class b/build/lib3270j/org/lib3270j/listener/ScreenUpdateListener.class index d589c15c7801709409eddb5a067f99cec8644153..706455b14bf147fdea6f9439738299365d2160ac 100644 GIT binary patch delta 17 Zcmeyx^oxn()W2Q(7#J8_HgXg(0su!|2C)DD delta 17 Zcmeyx^oxn()W2Q(7#J8FH*yp)0su!(2Ce`A diff --git a/build/lib3270j/org/lib3270j/protocol/DS3270Constants.class b/build/lib3270j/org/lib3270j/protocol/DS3270Constants.class index 6280c87f7b4810072fbdce6ac94af1b4605d1eda..1fdbea2390673139afb6d865ff6fff3ac318e8c4 100644 GIT binary patch delta 17 ZcmZ4IyUv&6)W2Q(7#J8_HgYUe0{}-g2M7QF delta 17 ZcmZ4IyUv&6)W2Q(7#J8FH*zdf0{}-R2L%8C diff --git a/build/lib3270j/org/lib3270j/protocol/TN3270EConstants.class b/build/lib3270j/org/lib3270j/protocol/TN3270EConstants.class index 23866402678c26b47aaa6f9080194c0a9a6b7244..0de4f6972d84c5e0f0cf70b09944fe2f5b2b5e98 100644 GIT binary patch delta 17 ZcmbQPFkOM;)W2Q(7#J8_HgfO_002G91=9ck delta 17 ZcmbQPFkOM;)W2Q(7#J8FH*)X`002F_1<(Kh diff --git a/build/lib3270j/org/lib3270j/protocol/TelnetConstants.class b/build/lib3270j/org/lib3270j/protocol/TelnetConstants.class index e9ecbdfe4e40c9fada55dfdc926a77a67e5e1a0d..f95779f11dd8039f003a8cbcbf08063090a7e49f 100644 GIT binary patch delta 17 ZcmbO)IbV|F)W2Q(7#J8_HgZho0RTS*23Y_A delta 17 ZcmbO)IbV|F)W2Q(7#J8FH*!qp0RTSs237z7 diff --git a/build/lib3270j/org/lib3270j/screen/ExtendedAttribute.class b/build/lib3270j/org/lib3270j/screen/ExtendedAttribute.class index eac570f04e063aa0f7bcb900e2ac18da0756abe6..63a37cfb2d827c1e686ba3cdc8b71f7bfeaa4060 100644 GIT binary patch delta 17 ZcmbQlHHnMk)W2Q(7#J8_Hga&W0suOK1&sgz delta 17 ZcmbQlHHnMk)W2Q(7#J8FH*#>X0suO51&ROw diff --git a/build/lib3270j/org/lib3270j/screen/ScreenBuffer.class b/build/lib3270j/org/lib3270j/screen/ScreenBuffer.class index aaf779b61bd2f01c6bc5f41038860bb9ff8fde62..728073b13cca1cd969257d832f5f823fe84c14b9 100644 GIT binary patch delta 592 zcmY+AO-NKx7>2)h#%Q3Rdo|NLW0>|!3Q9jVnKn^cX;#vrMH0lTq$$M^hhxOs@OgL(mzXcamvMk^;4*v*-oRbvMfe_Ggokhq&f#*L z$1CwLUW-R?m1WGv2{w{=fR|xv(-Qm$Z^4gY8lJ#Mn1G+~6lUN#>!NrPzQIfQ46oof zyoP^aYiV`Xuk3VwWf!EK*WtXQ&g*ktzw<2zc^HNf7;=7e5RZX#>ql*M;=bK<>=d!v zjJ?Lysd`GA?ValE6P12Z(!Oe|eOJBx;_3dV!TzdIrtK@)MD+DrzR~`vr4pE zaqZD^?bQmkXq8&EPWx1?Hq~msHtB$pI;aMKA6bA6;Y15>MO8UBl_U&S*fkj-Z>e84j26uy7(L`yb5)~4cN)Qqfl79?r zOhg9~F&Ip4VN`qrU%}8sJURKDoIK~8LC;ZY(SiiNU#YhhOoe9WEjQ@Td>9mHra}8wqcL$IA8~k7{#T2z3>zecRHQs z)mP6hQ}7L0y(nQHs@RWO4xo;MX3C!~A5}W0bX>^P;X ZUCf&iyT_G5DVNN(-R)Y}v|$)1`v)*lLNx#Y diff --git a/build/lib3270j/org/lib3270j/telnet/TelnetConnection.class b/build/lib3270j/org/lib3270j/telnet/TelnetConnection.class index 735db9f31f2b804d0079aea67649d72c32e247c7..bf938e2c67acaff493015c962de63280223ebfa4 100644 GIT binary patch literal 6087 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 Y>$%c;uCkt9v4LD}u5A=m;v(e#7r08m8UO$Q diff --git a/build/lib3270j/org/lib3270j/telnet/TelnetFSM$TN3270ESubmode.class b/build/lib3270j/org/lib3270j/telnet/TelnetFSM$TN3270ESubmode.class index 241800d5cf57fb39df3a7c78de4c17ba1be10c4b..89f0eb331a4ea9216ffd9b6b050297f782e3ac3c 100644 GIT binary patch delta 17 YcmZqYYUkoO^>5cc1_lO~jU4}306Pl?YXATM delta 17 YcmZqYYUkoO^>5cc1_lPljU4}306P2zXaE2J diff --git a/build/lib3270j/org/lib3270j/telnet/TelnetFSM.class b/build/lib3270j/org/lib3270j/telnet/TelnetFSM.class index 4a3210d68468c68cb591178db913e35baf827234..58aed75b3c66a8efdc2ca329d99c0cd89d6796fc 100644 GIT binary patch delta 19 bcmdnd#<-)6k>k|AUHcdq7+f}T7&rp}QZENL delta 19 bcmdnd#<-)6k>k|AUHcdq7#ufp7&rp}QXmI3 diff --git a/build_all.sh b/build_all.sh index f11c32f..936b249 100755 --- a/build_all.sh +++ b/build_all.sh @@ -1,37 +1,102 @@ #!/bin/bash set -e -echo "=== Building j3270 (Desktop JAR) and a3270 (Android APK) ===" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" -export JAVA_HOME="/Users/rudi/.sdkman/candidates/java/21.0.6-sem" -export ANDROID_HOME="/Users/rudi/Library/Android/sdk" +echo "=== Building j3270 Terminal Emulator ===" -GRADLE_BIN="/Users/rudi/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/bin/gradle" -BUILD_DIR="$(pwd)/build" +# 1. Locate Java toolchain +JAVAC_BIN="" +JAR_BIN="" -mkdir -p "$BUILD_DIR" - -echo "Building Android APK (a3270)..." -"$GRADLE_BIN" :a3270:assembleDebug - -echo "Building Desktop App JAR (j3270)..." -mkdir -p "$BUILD_DIR/lib3270j" "$BUILD_DIR/j3270" -"$JAVA_HOME/bin/javac" -d "$BUILD_DIR/lib3270j" $(find lib3270j/src -name "*.java") -"$JAVA_HOME/bin/javac" -cp "$BUILD_DIR/lib3270j" -d "$BUILD_DIR/j3270" $(find j3270/src -name "*.java") - -echo "Main-Class: org.pubvm.j3270.J3270App" > "$BUILD_DIR/MANIFEST.MF" -echo "" >> "$BUILD_DIR/MANIFEST.MF" - -"$JAVA_HOME/bin/jar" cvfm "$BUILD_DIR/j3270.jar" "$BUILD_DIR/MANIFEST.MF" \ - -C "$BUILD_DIR/lib3270j" . \ - -C "$BUILD_DIR/j3270" . - -# Copy APK to build/ -if [ -f "a3270/build/outputs/apk/debug/a3270-debug.apk" ]; then - cp "a3270/build/outputs/apk/debug/a3270-debug.apk" "$BUILD_DIR/a3270.apk" - echo "Copied a3270-debug.apk to $BUILD_DIR/a3270.apk" +if [ -n "$JAVA_HOME" ] && [ -x "$JAVA_HOME/bin/javac" ] && [ -x "$JAVA_HOME/bin/jar" ]; then + JAVAC_BIN="$JAVA_HOME/bin/javac" + JAR_BIN="$JAVA_HOME/bin/jar" +elif command -v javac >/dev/null 2>&1 && command -v jar >/dev/null 2>&1; then + JAVAC_BIN="$(command -v javac)" + JAR_BIN="$(command -v jar)" +else + # Check common SDKMAN / macOS / Linux JDK locations + POSSIBLE_HOMES=( + "$HOME/.sdkman/candidates/java/current" + "/Library/Java/JavaVirtualMachines"/*/Contents/Home + "/usr/lib/jvm/default-java" + "/usr/lib/jvm/java-21-openjdk"* + "/usr/lib/jvm/java-17-openjdk"* + "/usr/lib/jvm/java-11-openjdk"* + ) + for h in "${POSSIBLE_HOMES[@]}"; do + if [ -d "$h" ] && [ -x "$h/bin/javac" ] && [ -x "$h/bin/jar" ]; then + export JAVA_HOME="$h" + JAVAC_BIN="$JAVA_HOME/bin/javac" + JAR_BIN="$JAVA_HOME/bin/jar" + break + fi + done fi +if [ -z "$JAVAC_BIN" ] || [ -z "$JAR_BIN" ]; then + echo "ERROR: Java development tools (javac, jar) could not be found." + echo "Please install JDK 11+ or set JAVA_HOME." + exit 1 +fi + +echo "Using Java compiler: $JAVAC_BIN" +echo "Using JAR utility: $JAR_BIN" + +BUILD_DIR="$SCRIPT_DIR/build" +rm -rf "$BUILD_DIR/lib3270j" "$BUILD_DIR/j3270" "$BUILD_DIR/MANIFEST.MF" +mkdir -p "$BUILD_DIR/lib3270j" "$BUILD_DIR/j3270" + +# 2. Compile lib3270j +echo "Compiling lib3270j..." +LIB_SOURCES=() +while IFS= read -r -d '' file; do + LIB_SOURCES+=("$file") +done < <(find "$SCRIPT_DIR/lib3270j/src/main/java" -name "*.java" -print0) + +if [ ${#LIB_SOURCES[@]} -eq 0 ]; then + echo "ERROR: No sources found in lib3270j/src/main/java" + exit 1 +fi +"$JAVAC_BIN" -d "$BUILD_DIR/lib3270j" "${LIB_SOURCES[@]}" + +# 3. Compile j3270 +echo "Compiling j3270 application..." +APP_SOURCES=() +while IFS= read -r -d '' file; do + APP_SOURCES+=("$file") +done < <(find "$SCRIPT_DIR/j3270/src/main/java" -name "*.java" -print0) + +if [ ${#APP_SOURCES[@]} -eq 0 ]; then + echo "ERROR: No sources found in j3270/src/main/java" + exit 1 +fi +"$JAVAC_BIN" -cp "$BUILD_DIR/lib3270j" -d "$BUILD_DIR/j3270" "${APP_SOURCES[@]}" + +# Copy any resources if present +if [ -d "$SCRIPT_DIR/j3270/src/main/resources" ]; then + cp -r "$SCRIPT_DIR/j3270/src/main/resources/"* "$BUILD_DIR/j3270/" 2>/dev/null || true +fi +if [ -d "$SCRIPT_DIR/lib3270j/src/main/resources" ]; then + cp -r "$SCRIPT_DIR/lib3270j/src/main/resources/"* "$BUILD_DIR/lib3270j/" 2>/dev/null || true +fi + +# 4. Package executable JAR +echo "Packaging j3270.jar..." +cat << 'EOF' > "$BUILD_DIR/MANIFEST.MF" +Manifest-Version: 1.0 +Main-Class: org.pubvm.j3270.J3270App +Implementation-Title: j3270 +Implementation-Version: 0.1.0 +Created-By: j3270 build_all.sh +EOF + +"$JAR_BIN" cvfm "$BUILD_DIR/j3270.jar" "$BUILD_DIR/MANIFEST.MF" \ + -C "$BUILD_DIR/lib3270j" . \ + -C "$BUILD_DIR/j3270" . > /dev/null + echo "=== Build Complete ===" -echo "Artifacts in $BUILD_DIR:" -ls -lh "$BUILD_DIR"/*.jar "$BUILD_DIR"/*.apk 2>/dev/null || true +echo "Executable JAR created: $BUILD_DIR/j3270.jar" +ls -lh "$BUILD_DIR/j3270.jar" diff --git a/docs/gitea-runner.md b/docs/gitea-runner.md new file mode 100644 index 0000000..bffe611 --- /dev/null +++ b/docs/gitea-runner.md @@ -0,0 +1,59 @@ +# Gitea Actions Runner Configuration for j3270 + +This repository includes Gitea Actions workflows in `.gitea/workflows/build.yaml` to automatically: +1. Compile and run unit tests. +2. Build the standalone `j3270.jar` executable via `build_all.sh`. +3. Upload `j3270.jar` as a downloadable artifact. +4. Report test results and diagnostics. + +--- + +## 1. Setting up `act_runner` (Gitea Actions Runner) + +To run CI workflows on your Gitea instance: + +### Step 1: Download `act_runner` +Download the `act_runner` binary for your platform from the [Gitea act_runner releases](https://gitea.com/gitea/act_runner/releases). + +### Step 2: Register the Runner +Obtain a runner registration token from **Repository Settings -> Actions -> Runners** or **Admin -> Actions -> Runners**, then register: + +```bash +./act_runner register \ + --instance https://your-gitea-instance.com \ + --token \ + --name "j3270-runner" \ + --labels "ubuntu-latest:docker://node:18-bullseye,ubuntu-22.04:docker://node:18-bullseye" +``` + +### Step 3: Run the Runner Daemon +```bash +./act_runner daemon +``` + +Or using Docker Compose: +```yaml +version: '3.8' +services: + runner: + image: gitea/act_runner:latest + restart: always + environment: + - GITEA_INSTANCE_URL=https://your-gitea-instance.com + - GITEA_RUNNER_REGISTRATION_TOKEN= + - GITEA_RUNNER_NAME=j3270-runner + - GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:18-bullseye + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./data:/data +``` + +--- + +## 2. CI/CD Workflow Summary + +- **Trigger**: Pushes to `master`/`main`/`develop`, pull requests, and manual triggers via `workflow_dispatch`. +- **Artifacts**: + - `j3270-executable-jar`: Ready-to-run desktop terminal emulator jar (`java -jar j3270.jar`). + - `test-reports`: Full JUnit test reports and XML/HTML summaries. +- **Reporting**: On failure, automated diagnostic reports are generated and attached to the run summary. diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..7997892 --- /dev/null +++ b/gradlew @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$(dirname "$0")/build_all.sh" diff --git a/j3270/build.gradle b/j3270/build.gradle index c204ba4..e5db1d3 100644 --- a/j3270/build.gradle +++ b/j3270/build.gradle @@ -7,5 +7,17 @@ dependencies { } application { - mainClass = 'org.j3270.J3270App' + mainClass = 'org.pubvm.j3270.J3270App' +} + +jar { + manifest { + attributes 'Main-Class': 'org.pubvm.j3270.J3270App', + 'Implementation-Title': 'j3270', + 'Implementation-Version': archiveVersion + } + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } diff --git a/j3270/src/main/java/org/pubvm/j3270/ft/FileTransfer.java b/j3270/src/main/java/org/pubvm/j3270/ft/FileTransfer.java index affed6f..2cd90a4 100644 --- a/j3270/src/main/java/org/pubvm/j3270/ft/FileTransfer.java +++ b/j3270/src/main/java/org/pubvm/j3270/ft/FileTransfer.java @@ -194,19 +194,18 @@ public class FileTransfer implements FTCut.FTCutListener, FTDft.FTDftListener { // ========== FTCutListener / FTDftListener Callbacks ========== @Override - public void onTransferRunning() { - // Mode detection check - StackTraceElement[] st = Thread.currentThread().getStackTrace(); - boolean isDft = false; - for (StackTraceElement elem : st) { - if (elem.getClassName().contains("FTDft")) { - isDft = true; - break; - } - } + public void onCutRunning() { + handleTransferRunning(FTMode.CUT); + } + @Override + public void onDftRunning() { + handleTransferRunning(FTMode.DFT); + } + + private void handleTransferRunning(FTMode mode) { if (activeMode == FTMode.UNKNOWN) { - activeMode = isDft ? FTMode.DFT : FTMode.CUT; + activeMode = mode; log.info("FT mode established: " + activeMode); try { if (activeMode == FTMode.DFT) { diff --git a/j3270/src/main/java/org/pubvm/j3270/ui/TerminalPanel.java b/j3270/src/main/java/org/pubvm/j3270/ui/TerminalPanel.java index 81394bf..190daaf 100644 --- a/j3270/src/main/java/org/pubvm/j3270/ui/TerminalPanel.java +++ b/j3270/src/main/java/org/pubvm/j3270/ui/TerminalPanel.java @@ -492,6 +492,14 @@ public class TerminalPanel extends JPanel { // Clear bindKeyToMap(im, "CLEAR", org.pubvm.j3270.config.Settings.getKeyBinding("CLEAR", "alt C")); + // Extended 3270 functions + bindKeyToMap(im, "ERASE_INPUT", org.pubvm.j3270.config.Settings.getKeyBinding("ERASE_INPUT", "alt E")); + bindKeyToMap(im, "NEWLINE", org.pubvm.j3270.config.Settings.getKeyBinding("NEWLINE", "shift ENTER")); + bindKeyToMap(im, "DUP", org.pubvm.j3270.config.Settings.getKeyBinding("DUP", "alt D")); + bindKeyToMap(im, "FIELD_MARK", org.pubvm.j3270.config.Settings.getKeyBinding("FIELD_MARK", "alt M")); + bindKeyToMap(im, "ATTN", org.pubvm.j3270.config.Settings.getKeyBinding("ATTN", "alt A")); + bindKeyToMap(im, "SYSREQ", org.pubvm.j3270.config.Settings.getKeyBinding("SYSREQ", "alt S")); + // Copy/Paste bindings — Cmd+C / Cmd+V (macOS) or Ctrl+C / Ctrl+V (others) int shortcutMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx(); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, shortcutMask), "j3270-COPY"); @@ -514,6 +522,12 @@ public class TerminalPanel extends JPanel { am.put("j3270-BACK_SPACE", createAction(this::handleBackspace)); am.put("j3270-INSERT", createAction(this::handleInsert)); am.put("j3270-CLEAR", createAction(this::handleClear)); + am.put("j3270-ERASE_INPUT", createAction(this::handleEraseInput)); + am.put("j3270-NEWLINE", createAction(this::handleNewline)); + am.put("j3270-DUP", createAction(this::handleDup)); + am.put("j3270-FIELD_MARK", createAction(this::handleFieldMark)); + am.put("j3270-ATTN", createAction(this::handleAttn)); + am.put("j3270-SYSREQ", createAction(this::handleSysReq)); // Copy/Paste actions am.put("j3270-COPY", createAction(this::copySelection)); @@ -658,6 +672,47 @@ public class TerminalPanel extends JPanel { } } + private void handleEraseInput() { + if (client != null && client.getConnectionState().isFullSession()) { + client.eraseInput(); + refreshScreen(); + } + } + + private void handleNewline() { + if (client != null && client.getConnectionState().isFullSession()) { + client.newline(); + refreshScreen(); + } + } + + private void handleDup() { + if (client != null && client.getConnectionState().isFullSession()) { + client.dup(); + refreshScreen(); + } + } + + private void handleFieldMark() { + if (client != null && client.getConnectionState().isFullSession()) { + client.fieldMark(); + refreshScreen(); + } + } + + private void handleAttn() { + if (client != null && client.isConnected()) { + client.attn(); + } + } + + private void handleSysReq() { + if (client != null && client.isConnected()) { + client.sysReq(); + refreshScreen(); + } + } + private void refreshScreen() { repaint(); // Notify parent to update status bar too diff --git a/lib3270j/src/main/java/org/lib3270j/Telnet3270Client.java b/lib3270j/src/main/java/org/lib3270j/Telnet3270Client.java index 06119f2..4e9f1ef 100644 --- a/lib3270j/src/main/java/org/lib3270j/Telnet3270Client.java +++ b/lib3270j/src/main/java/org/lib3270j/Telnet3270Client.java @@ -196,6 +196,18 @@ public class Telnet3270Client { public void tab() { inputProcessor.tab(); } /** Back-tab to previous unprotected field. */ public void backTab() { inputProcessor.backTab(); } + /** Move cursor to next line. */ + public void newline() { inputProcessor.newline(); } + /** Erase all unprotected fields. */ + public void eraseInput() { inputProcessor.eraseInput(); } + /** Insert Duplicate order. */ + public void dup() { inputProcessor.dup(); } + /** Insert Field Mark order. */ + public void fieldMark() { inputProcessor.fieldMark(); } + /** Attention key. */ + public void attn() { inputProcessor.attn(); } + /** SysReq key. */ + public void sysReq() { inputProcessor.sysReq(); } /** Reset (unlock keyboard). */ public void reset() { inputProcessor.reset(); } } diff --git a/lib3270j/src/main/java/org/lib3270j/datastream/DataStreamProcessor.java b/lib3270j/src/main/java/org/lib3270j/datastream/DataStreamProcessor.java index 8acac23..6d9795b 100644 --- a/lib3270j/src/main/java/org/lib3270j/datastream/DataStreamProcessor.java +++ b/lib3270j/src/main/java/org/lib3270j/datastream/DataStreamProcessor.java @@ -816,8 +816,18 @@ public class DataStreamProcessor { } private void sendRequestedQueryReplies(byte[] codes) { - // For now, send all — refinement can come later - sendAllQueryReplies(); + byte[] qr = qrBuilder.buildQueryReplies(codes, screen.getMaxCols(), screen.getMaxRows(), + screen.getMaxCols() * screen.getMaxRows()); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < qr.length; i++) { + sb.append(String.format("%02x ", qr[i] & 0xFF)); + if ((i + 1) % 32 == 0) + sb.append("\n "); + } + log.info("Requested query reply (" + qr.length + " bytes):\n " + sb.toString().trim()); + if (outputSender != null) { + outputSender.send3270Data(qr); + } } // ========== Output helpers ========== diff --git a/lib3270j/src/main/java/org/lib3270j/datastream/QueryReplyBuilder.java b/lib3270j/src/main/java/org/lib3270j/datastream/QueryReplyBuilder.java index 74c63b9..df5bf52 100644 --- a/lib3270j/src/main/java/org/lib3270j/datastream/QueryReplyBuilder.java +++ b/lib3270j/src/main/java/org/lib3270j/datastream/QueryReplyBuilder.java @@ -70,13 +70,69 @@ public class QueryReplyBuilder { appendQueryReply(out, QR_REPLY_MODES, buildReplyModes()); // Distributed Data Management (DFT File Transfer) - // Hardcoded limit of 4096 is standard; the client can configure it during transfer. appendQueryReply(out, QR_DDM, buildDdm(4096)); // Implicit Partition appendQueryReply(out, QR_IMP_PART, buildImplicitPartition(maxCols, maxRows)); - log.info("Built " + out.size() + " bytes of query replies"); + log.info("Built " + out.size() + " bytes of all query replies"); + return out.toByteArray(); + } + + /** + * Build specific query replies in response to a Read Partition Query List (SF_RPQ_LIST). + * For any unsupported requested query code, emits a QR_NULL (0xFF) structured field + * matching x3270 sf.c behavior. + */ + public byte[] buildQueryReplies(byte[] requestedCodes, int maxCols, int maxRows, int bufferSize) { + if (requestedCodes == null || requestedCodes.length == 0) { + return buildAllQueryReplies(maxCols, maxRows, bufferSize); + } + + ByteArrayOutputStream out = new ByteArrayOutputStream(512); + out.write(AID_SF); + + for (byte codeByte : requestedCodes) { + int code = codeByte & 0xFF; + switch (code) { + case QR_SUMMARY: + appendQueryReply(out, QR_SUMMARY, buildSummary()); + break; + case QR_USABLE_AREA: + appendQueryReply(out, QR_USABLE_AREA, buildUsableArea(maxCols, maxRows, bufferSize)); + break; + case QR_ALPHA_PART: + appendQueryReply(out, QR_ALPHA_PART, buildAlphaPartitions(maxRows)); + break; + case QR_CHARSETS: + appendQueryReply(out, QR_CHARSETS, buildCharsets()); + break; + case QR_COLOR: + appendQueryReply(out, QR_COLOR, buildColor()); + break; + case QR_HIGHLIGHTING: + appendQueryReply(out, QR_HIGHLIGHTING, buildHighlighting()); + break; + case QR_REPLY_MODES: + appendQueryReply(out, QR_REPLY_MODES, buildReplyModes()); + break; + case QR_DDM: + appendQueryReply(out, QR_DDM, buildDdm(4096)); + break; + case QR_IMP_PART: + appendQueryReply(out, QR_IMP_PART, buildImplicitPartition(maxCols, maxRows)); + break; + case QR_RPQNAMES: + appendQueryReply(out, QR_RPQNAMES, new byte[0]); + break; + default: + // Unsupported query reply code — emit QR_NULL + appendQueryReply(out, QR_NULL, new byte[0]); + break; + } + } + + log.info("Built " + out.size() + " bytes of requested query replies for " + requestedCodes.length + " codes"); return out.toByteArray(); } diff --git a/lib3270j/src/main/java/org/lib3270j/ft/FTConfig.java b/lib3270j/src/main/java/org/lib3270j/ft/FTConfig.java index 97d1703..0bfa42f 100644 --- a/lib3270j/src/main/java/org/lib3270j/ft/FTConfig.java +++ b/lib3270j/src/main/java/org/lib3270j/ft/FTConfig.java @@ -225,84 +225,96 @@ public class FTConfig { // IND$FILE GET/PUT hostfile cmd.append("IND$FILE "); cmd.append(isReceive() ? "GET " : "PUT "); - cmd.append(hostFilename); + cmd.append(hostFilename != null ? hostFilename.trim() : ""); - // CMS/CICS options use ( prefix - if (hostType != HostType.TSO) { - cmd.append(" ("); - } else { - cmd.append(" "); - } + // Collect options into a separate buffer + StringBuilder opts = new StringBuilder(); // Mode if (isAscii()) { - cmd.append("ASCII"); + opts.append("ASCII"); } else if (hostType == HostType.CICS) { - cmd.append("BINARY"); + opts.append("BINARY"); } // CR/LF handling if (isAscii() && isCrFlag()) { - cmd.append(" CRLF"); - } else if (hostType == HostType.CICS) { - cmd.append(" NOCRLF"); + if (opts.length() > 0) opts.append(" "); + opts.append("CRLF"); + } else if (hostType == HostType.CICS && !isAscii()) { + if (opts.length() > 0) opts.append(" "); + opts.append("NOCRLF"); } // Append (send only) if (isAppend() && isSend()) { - cmd.append(" APPEND"); + if (opts.length() > 0) opts.append(" "); + opts.append("APPEND"); } - // TSO-specific send options + // Host-specific send options if (isSend()) { if (hostType == HostType.TSO) { if (recfm != RecordFormat.DEFAULT) { - cmd.append(" RECFM("); + if (opts.length() > 0) opts.append(" "); + opts.append("RECFM("); switch (recfm) { - case FIXED: cmd.append("F"); break; - case VARIABLE: cmd.append("V"); break; - case UNDEFINED: cmd.append("U"); break; + case FIXED: opts.append("F"); break; + case VARIABLE: opts.append("V"); break; + case UNDEFINED: opts.append("U"); break; default: break; } - cmd.append(")"); + opts.append(")"); if (lrecl > 0) { - cmd.append(" LRECL(").append(lrecl).append(")"); + opts.append(" LRECL(").append(lrecl).append(")"); } if (blksize > 0) { - cmd.append(" BLKSIZE(").append(blksize).append(")"); + opts.append(" BLKSIZE(").append(blksize).append(")"); } } if (units != AllocationUnit.DEFAULT) { - cmd.append(" SPACE(").append(primarySpace); + if (opts.length() > 0) opts.append(" "); + opts.append("SPACE(").append(primarySpace); if (secondarySpace > 0) { - cmd.append(",").append(secondarySpace); + opts.append(",").append(secondarySpace); } - cmd.append(")"); + opts.append(")"); switch (units) { - case TRACKS: cmd.append(" TRACKS"); break; - case CYLINDERS: cmd.append(" CYLINDERS"); break; - case AVBLOCK: cmd.append(" AVBLOCK(").append(avblock).append(")"); break; + case TRACKS: opts.append(" TRACKS"); break; + case CYLINDERS: opts.append(" CYLINDERS"); break; + case AVBLOCK: opts.append(" AVBLOCK(").append(avblock).append(")"); break; default: break; } } } else if (hostType == HostType.CMS) { if (recfm != RecordFormat.DEFAULT) { - cmd.append(" RECFM "); + if (opts.length() > 0) opts.append(" "); + opts.append("RECFM "); switch (recfm) { - case FIXED: cmd.append("F"); break; - case VARIABLE: cmd.append("V"); break; + case FIXED: opts.append("F"); break; + case VARIABLE: opts.append("V"); break; default: break; } if (lrecl > 0) { - cmd.append(" LRECL ").append(lrecl); + opts.append(" LRECL ").append(lrecl); } } } } // Additional options - if (otherOptions != null) { - cmd.append(" ").append(otherOptions); + if (otherOptions != null && !otherOptions.trim().isEmpty()) { + if (opts.length() > 0) opts.append(" "); + opts.append(otherOptions.trim()); + } + + // Apply options with host-specific prefix only if options are present + if (opts.length() > 0) { + if (hostType != HostType.TSO) { + cmd.append(" (").append(opts.toString()); + } else { + cmd.append(" ").append(opts.toString()); + } } return cmd.toString().trim(); diff --git a/lib3270j/src/main/java/org/lib3270j/ft/FTCut.java b/lib3270j/src/main/java/org/lib3270j/ft/FTCut.java index 2001f12..67c0592 100644 --- a/lib3270j/src/main/java/org/lib3270j/ft/FTCut.java +++ b/lib3270j/src/main/java/org/lib3270j/ft/FTCut.java @@ -21,7 +21,7 @@ public class FTCut { /** Callback for transfer events */ public interface FTCutListener { - void onTransferRunning(); + void onCutRunning(); void onTransferComplete(String errorMessage); void onTransferAborted(String errorMessage); void onBytesTransferred(long bytes); @@ -221,7 +221,7 @@ public class FTCut { xlateBufIx = 0; cutEof = false; cutAck(); - listener.onTransferRunning(); + listener.onCutRunning(); break; case SC_XFER_COMPLETE: diff --git a/lib3270j/src/main/java/org/lib3270j/ft/FTDft.java b/lib3270j/src/main/java/org/lib3270j/ft/FTDft.java index bc1079e..39abdfd 100644 --- a/lib3270j/src/main/java/org/lib3270j/ft/FTDft.java +++ b/lib3270j/src/main/java/org/lib3270j/ft/FTDft.java @@ -23,7 +23,7 @@ public class FTDft { /** Callback for transfer events (shared interface with CUT) */ public interface FTDftListener { - void onTransferRunning(); + void onDftRunning(); void onTransferComplete(String errorMessage); void onTransferAborted(String errorMessage); void onBytesTransferred(long bytes); @@ -178,7 +178,7 @@ public class FTDft { log.info("DFT: Open request for message stream"); } else { messageFlag = false; - listener.onTransferRunning(); + listener.onDftRunning(); } dftEof = false; diff --git a/lib3270j/src/main/java/org/lib3270j/input/InputProcessor.java b/lib3270j/src/main/java/org/lib3270j/input/InputProcessor.java index 25f2777..1e27004 100644 --- a/lib3270j/src/main/java/org/lib3270j/input/InputProcessor.java +++ b/lib3270j/src/main/java/org/lib3270j/input/InputProcessor.java @@ -4,6 +4,7 @@ import org.lib3270j.screen.ScreenBuffer; import org.lib3270j.screen.ExtendedAttribute; import org.lib3270j.charset.EbcdicTranslator; import org.lib3270j.telnet.TelnetFSM; +import org.lib3270j.protocol.TelnetConstants; import static org.lib3270j.protocol.DS3270Constants.*; import java.io.ByteArrayOutputStream; @@ -362,6 +363,82 @@ public class InputProcessor { deleteChar(); } + /** Erase All Unprotected fields (Erase Input key in 3270). */ + public void eraseInput() { + if (keyboardLocked) return; + screen.eraseAllUnprotected(); + screen.markAllChanged(); + } + + /** Move cursor to first unprotected field on next line (Newline key in 3270). */ + public void newline() { + if (keyboardLocked) return; + int cur = screen.getCursorAddress(); + int cols = screen.getCols(); + int rows = screen.getRows(); + int curRow = cur / cols; + int nextRow = (curRow + 1) % rows; + int target = nextRow * cols; + if (screen.isFormatted()) { + target = screen.findNextUnprotected(target == 0 ? (rows * cols - 1) : (target - 1)); + } + screen.setCursorAddress(target); + screen.markAllChanged(); + } + + /** Insert Duplicate (DUP) code and advance to next field. */ + public void dup() { + if (keyboardLocked) return; + int baddr = screen.getCursorAddress(); + byte faVal = screen.getFieldAttributeAt(baddr); + if (faIsProtected(faVal & 0xFF)) return; + + ExtendedAttribute ea = screen.getCell(baddr); + ea.ec = (byte) FCORDER_DUP; + ea.ucs4 = '*'; + int faAddr = screen.findFieldAttribute(baddr); + if (faAddr >= 0) { + screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY); + } + tab(); + } + + /** Insert Field Mark (FM) code. */ + public void fieldMark() { + if (keyboardLocked) return; + int baddr = screen.getCursorAddress(); + byte faVal = screen.getFieldAttributeAt(baddr); + if (faIsProtected(faVal & 0xFF)) return; + + ExtendedAttribute ea = screen.getCell(baddr); + ea.ec = (byte) FCORDER_FM; + ea.ucs4 = ';'; + int faAddr = screen.findFieldAttribute(baddr); + if (faAddr >= 0) { + screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY); + } + int size = screen.getRows() * screen.getCols(); + baddr = (baddr + 1) % size; + while (screen.getCell(baddr).isFieldAttribute()) { + baddr = (baddr + 1) % size; + } + screen.setCursorAddress(baddr); + screen.markAllChanged(); + } + + /** Attention key (sends Telnet IP). */ + public void attn() { + if (fsm != null && fsm.getConnectionState() != null && fsm.getConnectionState().isConnected()) { + byte[] ip = new byte[] { (byte) TelnetConstants.IAC, (byte) TelnetConstants.IP }; + fsm.sendRecord(ip); + } + } + + /** SysReq key. */ + public void sysReq() { + reset(); + } + /** Reset (unlock keyboard, cancel insert mode). */ public void reset() { setKeyboardLocked(false); diff --git a/lib3270j/src/main/java/org/lib3270j/screen/ScreenBuffer.java b/lib3270j/src/main/java/org/lib3270j/screen/ScreenBuffer.java index 658a14f..fae33f0 100644 --- a/lib3270j/src/main/java/org/lib3270j/screen/ScreenBuffer.java +++ b/lib3270j/src/main/java/org/lib3270j/screen/ScreenBuffer.java @@ -305,6 +305,7 @@ public class ScreenBuffer { private char getAplGraphic(int ec) { switch (ec) { + // Box-drawing line and corner characters (standard IBM 3270 GE / APL) case 0xA2: return '\u2500'; // Horizontal Line 's' -> '─' case 0x85: return '\u2502'; // Vertical Line 'e' -> '│' case 0xC5: return '\u250C'; // Top Left 'E' -> '┌' @@ -316,6 +317,24 @@ public class ScreenBuffer { case 0xC7: return '\u252C'; // T-Junction Top 'G' -> '┬' case 0xD7: return '\u2534'; // T-Junction Bottom 'P' -> '┴' case 0xCB: return '\u253C'; // Cross -> '┼' + + // Special math and APL symbols (matching x3270 cg.c / apl.c) + case 0x8C: return '\u2264'; // Less-than or equal '≤' + case 0xAE: return '\u2265'; // Greater-than or equal '≥' + case 0xBE: return '\u2260'; // Not equal '≠' + case 0xAD: return '['; // Left bracket + case 0xBD: return ']'; // Right bracket + case 0x8D: return '{'; // Left brace + case 0x9D: return '}'; // Right brace + case 0xB0: return '\u00B0'; // Degree '°' + case 0xB1: return '\u00B1'; // Plus-minus '±' + case 0xB2: return '\u00B2'; // Superscript 2 '²' + case 0xB3: return '\u00B3'; // Superscript 3 '³' + case 0xAF: return '\u00AF'; // Overbar '¯' + case 0xBA: return '\u03A9'; // Omega 'Ω' + case 0xBF: return '\u00B5'; // Micro 'µ' + case 0x5F: return '\u00AC'; // Not sign '¬' + default: return translator.ebcdicToUnicode(ec); } } diff --git a/lib3270j/src/main/java/org/lib3270j/telnet/TelnetConnection.java b/lib3270j/src/main/java/org/lib3270j/telnet/TelnetConnection.java index 32b96ff..1ce34c0 100644 --- a/lib3270j/src/main/java/org/lib3270j/telnet/TelnetConnection.java +++ b/lib3270j/src/main/java/org/lib3270j/telnet/TelnetConnection.java @@ -37,13 +37,26 @@ public class TelnetConnection { * Connect to the host. Blocks until connection is established or fails. */ public void connect() throws IOException { - log.info("Connecting to " + config.getHost() + ":" + config.getPort()); - socket = new Socket(); - socket.setKeepAlive(true); - socket.setOOBInline(true); - socket.setTcpNoDelay(true); - socket.connect(new InetSocketAddress(config.getHost(), config.getPort()), - config.getConnectTimeoutMs()); + if (config.isUseTls()) { + log.info("Connecting with TLS to " + config.getHost() + ":" + config.getPort()); + javax.net.ssl.SSLSocketFactory ssf = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(); + javax.net.ssl.SSLSocket sslSocket = (javax.net.ssl.SSLSocket) ssf.createSocket(); + sslSocket.setKeepAlive(true); + sslSocket.setOOBInline(true); + sslSocket.setTcpNoDelay(true); + sslSocket.connect(new InetSocketAddress(config.getHost(), config.getPort()), + config.getConnectTimeoutMs()); + sslSocket.startHandshake(); + socket = sslSocket; + } else { + log.info("Connecting to " + config.getHost() + ":" + config.getPort()); + socket = new Socket(); + socket.setKeepAlive(true); + socket.setOOBInline(true); + socket.setTcpNoDelay(true); + socket.connect(new InetSocketAddress(config.getHost(), config.getPort()), + config.getConnectTimeoutMs()); + } inputStream = new BufferedInputStream(socket.getInputStream(), READ_BUFFER_SIZE); outputStream = new BufferedOutputStream(socket.getOutputStream()); diff --git a/lib3270j/src/test/java/org/lib3270j/charset/EbcdicTranslatorTest.java b/lib3270j/src/test/java/org/lib3270j/charset/EbcdicTranslatorTest.java new file mode 100644 index 0000000..00f7528 --- /dev/null +++ b/lib3270j/src/test/java/org/lib3270j/charset/EbcdicTranslatorTest.java @@ -0,0 +1,66 @@ +package org.lib3270j.charset; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class EbcdicTranslatorTest { + + private final EbcdicTranslator translator = new EbcdicTranslator(); + + @Test + public void testBasicAlphanumericTranslation() { + assertEquals('A', translator.ebcdicToUnicode(0xC1)); + assertEquals('B', translator.ebcdicToUnicode(0xC2)); + assertEquals('Z', translator.ebcdicToUnicode(0xE9)); + assertEquals('a', translator.ebcdicToUnicode(0x81)); + assertEquals('z', translator.ebcdicToUnicode(0xA9)); + assertEquals('0', translator.ebcdicToUnicode(0xF0)); + assertEquals('9', translator.ebcdicToUnicode(0xF9)); + assertEquals(' ', translator.ebcdicToUnicode(0x40)); + } + + @Test + public void testReverseTranslation() { + assertEquals(0xC1, translator.unicodeToEbcdic('A')); + assertEquals(0x81, translator.unicodeToEbcdic('a')); + assertEquals(0xF0, translator.unicodeToEbcdic('0')); + assertEquals(0x40, translator.unicodeToEbcdic(' ')); + assertEquals(0x4B, translator.unicodeToEbcdic('.')); + assertEquals(0x6B, translator.unicodeToEbcdic(',')); + } + + @Test + public void testSpecialCharactersCP037() { + // [ and ] + assertEquals(0xBA, translator.unicodeToEbcdic('[')); + assertEquals(0xBB, translator.unicodeToEbcdic(']')); + assertEquals('[', translator.ebcdicToUnicode(0xBA)); + assertEquals(']', translator.ebcdicToUnicode(0xBB)); + + // { and } + assertEquals(0xC0, translator.unicodeToEbcdic('{')); + assertEquals(0xD0, translator.unicodeToEbcdic('}')); + assertEquals('{', translator.ebcdicToUnicode(0xC0)); + assertEquals('}', translator.ebcdicToUnicode(0xD0)); + + // Backslash \ + assertEquals(0xE0, translator.unicodeToEbcdic('\\')); + assertEquals('\\', translator.ebcdicToUnicode(0xE0)); + + // Caret ^ + assertEquals(0xB0, translator.unicodeToEbcdic('^')); + assertEquals('^', translator.ebcdicToUnicode(0xB0)); + + // Tilde ~ + assertEquals(0xA1, translator.unicodeToEbcdic('~')); + assertEquals('~', translator.ebcdicToUnicode(0xA1)); + } + + @Test + public void testStringRoundTrip() { + String test = "Hello World! 123 [TEST] {VAR} \\"; + byte[] ebcdic = translator.stringToEbcdic(test); + String restored = translator.ebcdicToString(ebcdic, 0, ebcdic.length); + assertEquals(test, restored); + } +} diff --git a/lib3270j/src/test/java/org/lib3270j/datastream/DataStreamProcessorTest.java b/lib3270j/src/test/java/org/lib3270j/datastream/DataStreamProcessorTest.java new file mode 100644 index 0000000..3488d0d --- /dev/null +++ b/lib3270j/src/test/java/org/lib3270j/datastream/DataStreamProcessorTest.java @@ -0,0 +1,69 @@ +package org.lib3270j.datastream; + +import org.junit.jupiter.api.Test; +import org.lib3270j.TerminalModel; +import org.lib3270j.charset.EbcdicTranslator; +import org.lib3270j.screen.ScreenBuffer; +import org.lib3270j.screen.ExtendedAttribute; +import static org.junit.jupiter.api.Assertions.*; +import static org.lib3270j.protocol.DS3270Constants.*; + +public class DataStreamProcessorTest { + + private final EbcdicTranslator translator = new EbcdicTranslator(); + private final ScreenBuffer screen = new ScreenBuffer(TerminalModel.IBM_3279_4, translator); + private final DataStreamProcessor processor = new DataStreamProcessor(screen, translator); + + @Test + public void testEraseWriteAndSBAOrder() { + // Build an EW record: + // Byte 0: CMD_EW (0x05) + // Byte 1: WCC (0xC3) - restore keyboard, reset MDT + // Byte 2: ORDER_SBA (0x11) + // Byte 3, 4: Encoded address for 0 (0x40, 0x40) + // Byte 5: ORDER_SF (0x1D) + // Byte 6: FA (0xC0) - unprotected + // Byte 7..11: EBCDIC "HELLO" (0xC8, 0xC5, 0xD3, 0xD3, 0xD6) + byte[] record = new byte[] { + (byte) CMD_EW, + (byte) 0xC3, + (byte) ORDER_SBA, + (byte) 0x40, (byte) 0x40, + (byte) ORDER_SF, + (byte) FA_PRINTABLE, + (byte) 0xC8, (byte) 0xC5, (byte) 0xD3, (byte) 0xD3, (byte) 0xD6 + }; + + processor.processRecord(record, 0, record.length, true); + + assertTrue(screen.isFormatted()); + assertTrue(screen.getCell(0).isFieldAttribute()); + + // Positions 1..5 should contain "HELLO" + assertEquals('H', screen.getCell(1).ucs4); + assertEquals('E', screen.getCell(2).ucs4); + assertEquals('L', screen.getCell(3).ucs4); + assertEquals('L', screen.getCell(4).ucs4); + assertEquals('O', screen.getCell(5).ucs4); + } + + @Test + public void testRepeatToAddressOrder() { + // Write record with RA from 0 to 10 with character 'A' (0xC1) + byte[] record = new byte[] { + (byte) CMD_EW, + (byte) 0xC3, + (byte) ORDER_SBA, + (byte) 0x40, (byte) 0x40, // Address 0 + (byte) ORDER_RA, + (byte) 0x40, (byte) 0x4A, // Address 10 (0x40, 0x4A) + (byte) 0xC1 // EBCDIC 'A' + }; + + processor.processRecord(record, 0, record.length, true); + + for (int i = 0; i < 10; i++) { + assertEquals('A', screen.getCell(i).ucs4, "Cell " + i + " should be 'A'"); + } + } +} diff --git a/lib3270j/src/test/java/org/lib3270j/datastream/QueryReplyBuilderTest.java b/lib3270j/src/test/java/org/lib3270j/datastream/QueryReplyBuilderTest.java new file mode 100644 index 0000000..478cfcf --- /dev/null +++ b/lib3270j/src/test/java/org/lib3270j/datastream/QueryReplyBuilderTest.java @@ -0,0 +1,55 @@ +package org.lib3270j.datastream; + +import org.junit.jupiter.api.Test; +import org.lib3270j.TerminalModel; +import org.lib3270j.charset.EbcdicTranslator; +import org.lib3270j.screen.ScreenBuffer; +import static org.junit.jupiter.api.Assertions.*; +import static org.lib3270j.protocol.DS3270Constants.*; + +public class QueryReplyBuilderTest { + + private final EbcdicTranslator translator = new EbcdicTranslator(); + private final ScreenBuffer screen = new ScreenBuffer(TerminalModel.IBM_3279_4, translator); + private final QueryReplyBuilder qrBuilder = new QueryReplyBuilder(screen); + + @Test + public void testBuildAllQueryReplies() { + byte[] replies = qrBuilder.buildAllQueryReplies(80, 43, 80 * 43); + assertNotNull(replies); + assertTrue(replies.length > 0); + assertEquals((byte) AID_SF, replies[0]); + } + + @Test + public void testBuildFilteredQueryRepliesWithSupportedCodes() { + byte[] requested = new byte[] { (byte) QR_DDM, (byte) QR_USABLE_AREA }; + byte[] replies = qrBuilder.buildQueryReplies(requested, 80, 43, 80 * 43); + + assertNotNull(replies); + assertEquals((byte) AID_SF, replies[0]); + + // First SF should be DDM: length (2 bytes), SFID_QREPLY (0x81), QR_DDM (0x95) + int len1 = ((replies[1] & 0xFF) << 8) | (replies[2] & 0xFF); + assertEquals(0x81, replies[3] & 0xFF); + assertEquals(QR_DDM, replies[4] & 0xFF); + + // Second SF should be Usable Area + int pos2 = 1 + len1; + int len2 = ((replies[pos2] & 0xFF) << 8) | (replies[pos2 + 1] & 0xFF); + assertEquals(0x81, replies[pos2 + 2] & 0xFF); + assertEquals(QR_USABLE_AREA, replies[pos2 + 3] & 0xFF); + } + + @Test + public void testBuildFilteredQueryRepliesWithUnsupportedCode() { + byte[] requested = new byte[] { (byte) 0x77 }; // Unsupported code + byte[] replies = qrBuilder.buildQueryReplies(requested, 80, 43, 80 * 43); + + assertNotNull(replies); + assertEquals((byte) AID_SF, replies[0]); + // Should emit QR_NULL (0xFF) + assertEquals(0x81, replies[3] & 0xFF); + assertEquals(QR_NULL, replies[4] & 0xFF); + } +} diff --git a/lib3270j/src/test/java/org/lib3270j/ft/FTConfigTest.java b/lib3270j/src/test/java/org/lib3270j/ft/FTConfigTest.java new file mode 100644 index 0000000..1bccfa8 --- /dev/null +++ b/lib3270j/src/test/java/org/lib3270j/ft/FTConfigTest.java @@ -0,0 +1,83 @@ +package org.lib3270j.ft; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FTConfigTest { + + @Test + public void testTsoReceiveAscii() { + FTConfig config = new FTConfig(); + config.setHostType(FTConfig.HostType.TSO); + config.setDirection(FTConfig.Direction.RECEIVE); + config.setHostFilename("MY.DATASET"); + config.setTransferMode(FTConfig.TransferMode.ASCII); + config.setCrAction(FTConfig.CrAction.REMOVE); + + assertEquals("IND$FILE GET MY.DATASET ASCII CRLF", config.buildCommand()); + } + + @Test + public void testTsoReceiveBinary() { + FTConfig config = new FTConfig(); + config.setHostType(FTConfig.HostType.TSO); + config.setDirection(FTConfig.Direction.RECEIVE); + config.setHostFilename("'USER.BIN.DATA'"); + config.setTransferMode(FTConfig.TransferMode.BINARY); + + assertEquals("IND$FILE GET 'USER.BIN.DATA'", config.buildCommand()); + } + + @Test + public void testTsoSendFixedRecfm() { + FTConfig config = new FTConfig(); + config.setHostType(FTConfig.HostType.TSO); + config.setDirection(FTConfig.Direction.SEND); + config.setHostFilename("MY.DATASET"); + config.setTransferMode(FTConfig.TransferMode.ASCII); + config.setCrAction(FTConfig.CrAction.REMOVE); + config.setRecfm("F"); + config.setLrecl("80"); + config.setBlksize("3120"); + + assertEquals("IND$FILE PUT MY.DATASET ASCII CRLF RECFM(F) LRECL(80) BLKSIZE(3120)", config.buildCommand()); + } + + @Test + public void testCmsReceiveBinaryNoNakedParen() { + FTConfig config = new FTConfig(); + config.setHostType(FTConfig.HostType.CMS); + config.setDirection(FTConfig.Direction.RECEIVE); + config.setHostFilename("PROFILE EXEC A"); + config.setTransferMode(FTConfig.TransferMode.BINARY); + + // In CMS, binary receive has no options, so NO open parenthesis '(' should be generated! + assertEquals("IND$FILE GET PROFILE EXEC A", config.buildCommand()); + } + + @Test + public void testCmsReceiveAscii() { + FTConfig config = new FTConfig(); + config.setHostType(FTConfig.HostType.CMS); + config.setDirection(FTConfig.Direction.RECEIVE); + config.setHostFilename("TEST DATA A"); + config.setTransferMode(FTConfig.TransferMode.ASCII); + config.setCrAction(FTConfig.CrAction.REMOVE); + + assertEquals("IND$FILE GET TEST DATA A (ASCII CRLF", config.buildCommand()); + } + + @Test + public void testCmsSendWithRecfmAndLrecl() { + FTConfig config = new FTConfig(); + config.setHostType(FTConfig.HostType.CMS); + config.setDirection(FTConfig.Direction.SEND); + config.setHostFilename("TEST FILE A"); + config.setTransferMode(FTConfig.TransferMode.ASCII); + config.setCrAction(FTConfig.CrAction.REMOVE); + config.setRecfm("V"); + config.setLrecl("132"); + + assertEquals("IND$FILE PUT TEST FILE A (ASCII CRLF RECFM V LRECL 132", config.buildCommand()); + } +} diff --git a/lib3270j/src/test/java/org/lib3270j/protocol/DS3270ConstantsTest.java b/lib3270j/src/test/java/org/lib3270j/protocol/DS3270ConstantsTest.java new file mode 100644 index 0000000..d7831f9 --- /dev/null +++ b/lib3270j/src/test/java/org/lib3270j/protocol/DS3270ConstantsTest.java @@ -0,0 +1,51 @@ +package org.lib3270j.protocol; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import static org.lib3270j.protocol.DS3270Constants.*; + +public class DS3270ConstantsTest { + + @Test + public void test12BitAddressRoundTrip() { + int rows = 24; + int cols = 80; + int maxAddr = rows * cols; // 1920 + + for (int addr = 0; addr < maxAddr; addr += 17) { + byte[] encoded = encodeAddress(addr, rows, cols); + assertEquals(2, encoded.length); + int decoded = decodeAddress(encoded[0] & 0xFF, encoded[1] & 0xFF); + assertEquals(addr, decoded, "Address " + addr + " failed round trip"); + } + } + + @Test + public void test14BitAddressRoundTrip() { + int rows = 60; + int cols = 80; // 4800 (> 4096, triggers 14-bit format) + int maxAddr = rows * cols; + + for (int addr = 0; addr < maxAddr; addr += 123) { + byte[] encoded = encodeAddress(addr, rows, cols); + assertEquals(2, encoded.length); + int decoded = decodeAddress(encoded[0] & 0xFF, encoded[1] & 0xFF); + assertEquals(addr, decoded, "14-bit address " + addr + " failed round trip"); + } + } + + @Test + public void testFieldAttributePredicates() { + int protectedHighMod = FA_PRINTABLE | FA_PROTECT | FA_INT_HIGH_SEL | FA_MODIFY; + assertTrue(faIsProtected(protectedHighMod)); + assertTrue(faIsHigh(protectedHighMod)); + assertTrue(faIsModified(protectedHighMod)); + assertFalse(faIsNumeric(protectedHighMod)); + assertFalse(faIsSkip(protectedHighMod)); + + int skipField = FA_PRINTABLE | FA_PROTECT | FA_NUMERIC; + assertTrue(faIsSkip(skipField)); + assertTrue(faIsProtected(skipField)); + assertTrue(faIsNumeric(skipField)); + } +} diff --git a/lib3270j/src/test/java/org/lib3270j/screen/ScreenBufferTest.java b/lib3270j/src/test/java/org/lib3270j/screen/ScreenBufferTest.java new file mode 100644 index 0000000..c6a72eb --- /dev/null +++ b/lib3270j/src/test/java/org/lib3270j/screen/ScreenBufferTest.java @@ -0,0 +1,64 @@ +package org.lib3270j.screen; + +import org.junit.jupiter.api.Test; +import org.lib3270j.TerminalModel; +import org.lib3270j.charset.EbcdicTranslator; +import static org.junit.jupiter.api.Assertions.*; +import static org.lib3270j.protocol.DS3270Constants.*; + +public class ScreenBufferTest { + + private final EbcdicTranslator translator = new EbcdicTranslator(); + private final ScreenBuffer screen = new ScreenBuffer(TerminalModel.IBM_3279_4, translator); + + @Test + public void testScreenInitialization() { + assertEquals(24, screen.getRows()); + assertEquals(80, screen.getCols()); + assertEquals(43, screen.getMaxRows()); + assertEquals(80, screen.getMaxCols()); + assertFalse(screen.isFormatted()); + } + + @Test + public void testScreenEraseAlternate() { + screen.erase(true); + assertTrue(screen.isScreenAlt()); + assertEquals(43, screen.getRows()); + assertEquals(80, screen.getCols()); + + screen.erase(false); + assertFalse(screen.isScreenAlt()); + assertEquals(24, screen.getRows()); + assertEquals(80, screen.getCols()); + } + + @Test + public void testFieldNavigationAndFindAttribute() { + screen.erase(false); + // Place field attribute at position 0 (unprotected) + screen.setCellFA(0, (byte) FA_PRINTABLE); + // Place field attribute at position 20 (protected) + screen.setCellFA(20, (byte) (FA_PRINTABLE | FA_PROTECT)); + + assertTrue(screen.isFormatted()); + assertEquals(0, screen.findFieldAttribute(5)); + assertEquals(20, screen.findFieldAttribute(25)); + + // findNextUnprotected + int nextUnprot = screen.findNextUnprotected(0); + assertEquals(1, nextUnprot); + } + + @Test + public void testGraphicEscapeTranslation() { + screen.erase(false); + // Set cell at position 10 to horizontal line (0xA2) with CS_GE (0x04) + ExtendedAttribute ea = screen.getCell(10); + ea.ec = (byte) 0xA2; + ea.cs = (byte) CS_GE; + + screen.translateToUnicode(); + assertEquals('\u2500', ea.ucs4); // '─' + } +} diff --git a/run.sh b/run.sh index 36a7539..8717bcd 100755 --- a/run.sh +++ b/run.sh @@ -3,43 +3,14 @@ # Usage: ./run.sh [host] [port] [model] # Example: ./run.sh mainframe.example.com 23 4 -JAVA_DIR="$(cd "$(dirname "$0")" && pwd)" -BUILD_DIR="$JAVA_DIR/build" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +BUILD_DIR="$SCRIPT_DIR/build" -# Always rebuild to pick up changes -echo "Building lib3270j..." -rm -rf "$BUILD_DIR" -mkdir -p "$BUILD_DIR/lib3270j" "$BUILD_DIR/j3270" +# Build if jar is missing or rebuild requested +if [ ! -f "$BUILD_DIR/j3270.jar" ]; then + "$SCRIPT_DIR/build_all.sh" +fi -# Compile libraries -find "$JAVA_DIR/lib3270j/src" -name "*.java" -print0 | xargs -0 javac -d "$BUILD_DIR/lib3270j" - -echo "Building j3270..." -find "$JAVA_DIR/j3270/src" -name "*.java" -print0 | xargs -0 javac -cp "$BUILD_DIR/lib3270j" -d "$BUILD_DIR/j3270" - -echo "Build complete." - -# --------------------------------------------------------- -# NEW: Package the shareable JAR -# --------------------------------------------------------- -echo "Packaging j3270.jar..." - -# 1. Create the Manifest file indicating the entry point -# Note: The manifest file must end with a new line or carriage return -echo "Main-Class: org.pubvm.j3270.J3270App" > "$BUILD_DIR/MANIFEST.MF" -echo "" >> "$BUILD_DIR/MANIFEST.MF" - -# 2. Build the JAR using both compiled directories -jar cvfm "$BUILD_DIR/j3270.jar" "$BUILD_DIR/MANIFEST.MF" \ - -C "$BUILD_DIR/lib3270j" . \ - -C "$BUILD_DIR/j3270" . - -echo "Executable JAR created at: $BUILD_DIR/j3270.jar" - -# --------------------------------------------------------- -# Run the newly packaged JAR -# --------------------------------------------------------- -#exec java -Dapple.laf.useScreenMenuBar=true \ -# -Dapple.awt.application.name=j3270 \ -# -jar "$BUILD_DIR/j3270.jar" "$@" -nohup java -jar build/j3270.jar & +exec java -Dapple.laf.useScreenMenuBar=true \ + -Dapple.awt.application.name=j3270 \ + -jar "$BUILD_DIR/j3270.jar" "$@" diff --git a/settings.gradle b/settings.gradle index 0d6bc4d..27331d1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,4 +2,3 @@ rootProject.name = 'j3270-project' include 'lib3270j' include 'j3270' -include 'a3270'