From bda8ea953062439e7c64bab2cefe0d5df07a4047 Mon Sep 17 00:00:00 2001 From: Rudi Date: Tue, 21 Apr 2026 15:34:11 -0400 Subject: [PATCH] IND$FILE receiving --- bugs.txt | 1 - build/j3270/org/pubvm/j3270/J3270App$1.class | Bin 960 -> 960 bytes build/j3270/org/pubvm/j3270/J3270App$2.class | Bin 878 -> 3088 bytes build/j3270/org/pubvm/j3270/J3270App.class | Bin 15928 -> 16789 bytes .../org/lib3270j/Telnet3270Client.class | Bin 6272 -> 6466 bytes .../datastream/DataStreamProcessor.class | Bin 13935 -> 14246 bytes .../datastream/QueryReplyBuilder.class | Bin 3772 -> 3971 bytes .../org/lib3270j/input/InputProcessor.class | Bin 5610 -> 6652 bytes .../org/lib3270j/screen/ScreenBuffer.class | Bin 6327 -> 6938 bytes .../main/java/org/pubvm/j3270/J3270App.java | 76 ++++++++++++ .../java/org/lib3270j/Telnet3270Client.java | 8 ++ .../datastream/DataStreamProcessor.java | 14 +++ .../datastream/QueryReplyBuilder.java | 18 +++ .../org/lib3270j/input/InputProcessor.java | 109 ++++++++++++++++++ .../org/lib3270j/screen/ScreenBuffer.java | 55 +++++++++ 15 files changed, 280 insertions(+), 1 deletion(-) diff --git a/bugs.txt b/bugs.txt index 8b13789..e69de29 100644 --- a/bugs.txt +++ b/bugs.txt @@ -1 +0,0 @@ - diff --git a/build/j3270/org/pubvm/j3270/J3270App$1.class b/build/j3270/org/pubvm/j3270/J3270App$1.class index e8a51ce4acce9fd0a35b30945d7b43e4e9a36fa3..8ca98afbfd7d3548fa1daad88d0cae92998bace6 100644 GIT binary patch delta 45 zcmX@Wet><$TP8-k$?urL1RWXJ8Jrjd8JroU8C)1t8C)laGF!2FF>o_@PoBc80{{-d B3LXFe delta 45 zcmX@Wet><$TP8-U$?urL1Z^4E8SEGY8SELP85|f?85}2vGF!2_F>o`uPoBc80{{#t B3H|^8 diff --git a/build/j3270/org/pubvm/j3270/J3270App$2.class b/build/j3270/org/pubvm/j3270/J3270App$2.class index de1d926f0384bf396de6a87bc5f6cd4d37c90499..5113d3fcd68b2b38fd2ca82a055035b4e5ed598b 100644 GIT binary patch literal 3088 zcmcImSyLQU7(I7bdYCjYObAJc2}3f8EXp8JqZtw<0~l~1Bp@+~G4{+{prNPl*xf*I zjr+d6>2L7GC4orPQp-xIl?Rsxm*3&D<#)S#pc5ckRykGOx98sPo_oIYE!{u-wfrT3 zUc6U@5XuyktEfOFL!+=Jlf~KWTp>BVzw1C}a!}rTip4}1L!~pN+lfxH%4}qoiXe=H;_6>-W z64ltD;uh>=*k*HQNH+}K9uvJhr|0>Y7!k$UB13r#;Y-^l6>(`B4cNLi3U)Kp1vMJe z3*3rP023Lmg^cGlNAsi7sp1Y$Mk(a$uy8oT7C%02 zEPUl#$75i)Q^j4ln_+7~o8f6;<}~M|?o81W9b`JT+&kzy6x=N}y+_5pE~lI!Y#v66 zge-B2%QP^!!xbDN^q{_j$BM3;n#m*R#(gU8M-Oear^Nga<)${tdv(nalMLPeJ+GvD zA*7{yy$t)-6CjYEz9wPxGt{j3&}Uh~lAaz_F@Qmah;FAfGsg{?1XN+CtB}Evf?*ZM zFhZHQQC2Xh6S~1m$PCSEihROJ_6JNTOHN=^!I+ANq{vM-Lf>FdrJr-DMQ{=iD>$X% zG|o^rSt-n5F80a1WT=|t&ZwBRa+D+WEx|B*{lrbxvnn2y49aas6cs#1N%Id}5r+HA><<&@%dKCP9eOWAuJXirvH zj|5iCj2Ap=F;_nESsh`i^t3QLOLAgd!N zYpF!S!`ya`QcEV8jtJq{j-?g7t#%ZJ+6v4^s1VMpFi;5N32Npno|O`;ba~PsjHk%| zlK1g`jkx6x##wD8o{8XDTu|_wis$hH!)`aB++LuYb7F=kGp-tRrZz#HQdkQNnkA&)?)xJgS*_gWHLW5HqvIb>0tw8oh4g*g}y9&Hw$PcjV3Y1}niwmT5eyJ3K{_ zycEI9ctydhvb(&-kXR#$*H65D+Fugo{r_JEzmJ^>$mUn_&0M1HG+@%3DQ#%B&8fQT zgPB$;U|NvPdVy5%4#TdSn^ME_qE0X75PjD$oTD)$KLdbhRDLnhJ6NG>6EvF7 z($j9&0*JJ=Ujl1eg7PuVGUVu9Nh9zf@^r7D#|u%K;85u=nqR`^OK6~phO$L8`s1!EXg*cP{%l*u9$*Q#X4)6gwt85jOYS2y;1le^r`U%@ z9Kh#D;R`x%V&&9>bf%KV@ zG$h-WPy8&6GK8HTe7i^dfe+A3PwXNdyo#;<(;|*jz{bBq^CBL((UT?Yz-9Wh`wU53 zDIJxhgfj^|1c4%r<@9EM9JV`?fkWdqionx!&0>zO7m3V*dw&wI(`qBFNOJObj$Ykw NR94|lyp4CU{co)?GHL(- literal 878 zcmb7C+iuf95IyUpaS~kWmO|5VX(@&zTxtlQN`Me0O`FihesG`lTk9*YAg#71==)k6!YDJ$6OFkQ>`x%tMApmFoWP8N;6zkVipA zQNcMV3`>*ouM!a(qmfo)c%oVF-N+fV?U6?3v{a^+%GCbK)J~Tv?Y9ba)$cgUTHA^*(rT;GkJ zr)A$TWb2~G8D@4}pBwREmj@lY>yc-gn&!NtXZweXmWTs~Z@DRX{KRWX2?}rcjweED zcAG~7(UVbOD4o_9l40;LYKo8)DJ|dULEW>%kcTqvGAvKVO^g$jn;0l{g(l)E%_dVz z$xI|W@+@P8>~%sPkf9y0{vGV-XYniKFUdds4YOJE6S6xgTp&1g0+$HpQuvv|3z)|O zE~11RxJ9;%8Xl9Cuu6$@NqY8?IA9jJJ(-yH{{>lSL`b+vp&(SIwVHa$xJ`DBI0<)f M@5LhNrccfP0fSZ0cK`qY diff --git a/build/j3270/org/pubvm/j3270/J3270App.class b/build/j3270/org/pubvm/j3270/J3270App.class index 3f7ee53c4e2a4fe6272ceccfa9733fa3741f3454..fe40d61c8cb8e982427fef42d038dbcbd4cb8d25 100644 GIT binary patch delta 8251 zcmahu30#zA`p@%xGkn9$3l1|}!y%&}#|((~fu^E(prT|dVOb3@;26%}FnCnkblP@o zYF~$1ZP&K8wcDny13|g9wPtN)Zs%@ymf2xt*{$(^-fuoY+y1{mzW09a>wWFI=%&lj z`pi$=djOzHS+K+UkmYpytU#;Zy*TU(wm193!-o%dfE6}b;2#L4IsVo*3p|M+=K0sV zYkX~OfnZC!18j1`QwZkDhEPYu0RlEz`SXl`f5Efb7naek{Q_QqAqYwB{>ZFQ*xwQk zbp)GK__sDWF)Ql;n5}RC^5kc~6<&qcRQQj8|HA89TjDg!tMI0Px8QB9D>2{pF1)A0 zK>_c>2il>;*_H$Fp@75ikv7&k!jc4^2sjNs)mB+6c;zzzpTig0U#%0ZiA>Lz0=|M{ znlH&4wZJzBWivv-puaKVj)dGk_li(^#NFU;X6oD>?aY|_R7qIiJEng@DB?Db`I-J7 z5HeQ!*7(+yx3A?R<#QLdMFOEbi4r7AN0!+wPX z$!(~`iZ)D^cn2YjDV{GSR_ZwF##S zT!d$6Q`N z<_iQ_o{O+uU_{E;#xkB23P#QdT;R9j8eFU5I)Ur)0;bI%Tl>eLj1g<_B7wh=)5a^*qoA;;{TbQy+U#T^27O3|4b{QfovWJtB`-sN-#sksv& z?UeAf*uO4P$?COYjAJs!uv=h{^f@V^nN5L+kDX4%M>!G>I5CUff`4ZltqBD~?QOnB zf0Gp-!N;W$aN5XV{!){wrTfM3NrC^wy_zL`$oLosd`7ch#b-dJIX69GDg+OIZ7JNsxjTU^5-J`6G{jNFC zV#D|G0~HSmXva7DEK7$U2fYyw3;YN_)`FQ+EgSJufk*H&ZFlBK9(*D2D1NDZmN`24 z7=EqdHv+#!PA3^!tt&&DD#m5UZsFPJT@>4dH#%v@ZC<(u0jrf|H=Jdq-N!#`GN|hGnxGB0ud_}QjtozDI~9;(!S6;KNLE@GT1cB z?{BrxaBX9Dkz*r`5HwP5Ff|8)ehZD(_GRbgkL7VB+~Jof#tWJtT~AtLZ8+5C4@cHp zs6w;m0;86qZGgq}m*2}qBf@aek?RPou zocC$2pm{QwnA&9&p&DAC(n3MCbh`F*&P0x(N^U{&5?ZQKouD(Ro)fEfUT&7QFn73R z37sYAY+9~enLEN(Lg%u*635I@Z1`!~%ei@$3ThD4NKM-D+?wQOYEfy0pa89mXJ;xH zt*sthWoxBXDg_0FxYT)U@RWHKv|3PDO4`AauIz|}j7S?#SQQiz)FEm9*|MH*a0AWY z=t=7Yt)~mL>bxnoi|98hT`VYyPVI)giIu3(rGhS#v0+ds$Uf)y2bZ)p`6BGUu#Bz{ zbfsJq9s1lE5nrT(i@hwmiUSw@7GWaum!}PK=OkZ4*Q#`#px;Rm=V+DrIocApV`dd? z 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 b%uYz8R;*CwOmk7QoVHt(YGs~M1FruA8i+(N delta 7269 zcmaJl34D`9_V2wU{nBJe)1+;hHoa&|OVV=4U6DiXqu2!jtIH9ZPy%gI(iB16jR)(! zUV{p_>$zBVbv;1S*sixC9-t_z?CP%T?W(J;Co01K&G*F?U4Q?k$v1Ci-ZAf<@7fD6 zeU9y!qrDFSz;x5ldu{hv$C_t{+d8c9D1w+5-c;Tg?C6L@TRUA~gR89Y1cGJis#sUT z1$MZ~1_GWEuos?|_Uuu<=LGD73WPzO;l#{XJlq{R9^b!f^t};{=&<&R%t*z==wcR#wTWkwjbA1`BbL zz{yGkduvxP9tww=f-UPboQmLF8{7~a-WH6u4nK8GJRA(Uz`?GB(^=iDGbL-iv0%d~ zI8(_vORmfvHfRdWQiSIUoQEK9$n{RFKz5vkxIo}3ij@`*cLXC*Hq%1+{J>S?Xve6A zF@YUQQH8Ri*zMKP*>@t*)iD=1F)rY0>_ljjNArBL)xFT(g&Q>e=RVnC~Id1<57-J8=i;fYj}ac&G;L+EU!tp@FGS3VuaDP zGH5Q9ujF|KxpA!v^6)aXa=H92Z`Fx1hEI*hgPZ0@I00Z97F+Qztj+rRPj-Lwr zFTTu%35G(mgV9i1IIeQa=K{Y_zLe7z3x;OKW9^(AXGWA=zmli;JOU5nHyVB`@H;#r z!wCSGsEFFE4k##k{VY#~a$8AR zrI+DEyh~?MEXb##PdQ~%Jk}A8CpKBBRK8kL<}T-grJLUcg*2G`o+{;_(vjuxEnALi z1l6katwlO#w09&nsX4!(0M*ImrRBc8G)&NNl~&B1Dxc6uIzgjRf=1IAxxI7(j*|yV zs~i)kL8B7|O{9}lsu)?u>7s0ybplNmG=-+hC1oS5Su~Bknx@Mu%EnneG*i$lnl1O0 zHQMLWJdNfHYNQ2nnJAGbmru7h(JwSwC}g9|GY2)P+iBe7`t>LB|SXM=Xy!1sI(F?3Dof2<5o+aRXMr4Cm1o|L;Ag-uLsfy@ zJFD{0nG=gHYKe!#(Z#yLL$bFz%vr5`NFm<4d%}CK+vCb=pzOFN6;M%Wzi=J`czOK{l!Y3sY;--ZTXw$ZYjqG zWcj{O8@?2jhH5K)EzcTSQb*sYxo-va&}~*a!g={P({hrJ#T&{MYRF2*<*H3C zK@U)`)s!dK`imR|Hj~FxsF}RNRAegNeU0COjuM-x)KsRK$~g{96>>-50Q*P|$45NT z)p2roO>jdb7S~MdPSx^2pgK!4)gYAeuvFI8yWH2)Hm+txs+-_0oAlnRu|8n0QfUXO z0qF*LS^fEQe#ff~+!+B>LKc*9XI&01r~(h~sf8M-6zp8;cRiGXiyACmi{JGK`Fr6pfE36ST)$LJJ+7vn?4JsFEjm&l1$*vg&PYm% zwNS*r&0vMo!3$@=NLbENtblp2l1D*U#h(PUz(zjFWw3@Lc^xFTLL1xx?eGvp;Q++> ze$WN4!v^>q&PEN+MF(7f1+W>1!f&`0y%49t#W))-;UirFmnxMq%gKBo6FjQ6dF6=e_d!RT4&(__;TMS7Z`u58q8^-+>O5h5nb}d|&p|+K&S$ONhKG?6Q zz0hFxniV}~t@`N14#=jFdC3=jH-gJ+_NC#a6#Oj>|46}GUUPjfyr(qvf!C}!lCGN} z%nRTe*1ZHDY+>D83uBn&c(@)W@pC$CW7}<~-}5dj;1QUv@x6&u>}SNflx0?joYDs$ zH~V|w)A`AO|30v%;B)oNO~F?IHsv>Llkc}dNg##T=&wtm<$kcMr~Q7&RZoX9m?4y( z#R0gPMY{!Z;Z`=|~ewBkT8$0l_9~d>JB@f~IC98_seO6UymB z7fXhD^H~tgA8-sq-FGpcyV>pUVYHj+7KJQFjw(nh*U91#0ocv<;R{iQ>}z1w$zJT| zv*@Sbr#giK(^#OOG#2|)SnBV?3N{s1-jCJ(`}mu}p_ykr>t=}>r*I?>ml?w`I!#5P zH;H4}TG*iWrSPOaoYL&?#ghTtIIRa~q;R&eGH2<)fldY|i`&DNO7p0XBlj~kY8R_{s#t^5`?_!`~z?g$s8sO03i!>|w4RFJoPz-Nzj(MAn`3`54 z_jDtg07tO>xc(V>PQn6Kw3%^-7?hh%a$23UeZ?!&xG0UylhkZU3V&&=o;09^0zFJV zV7?!6*7=Av@Cl#jQzm;W-Ig)p%y9 z-JYQ}J|kJOP!X%^#Y-|m_2Q*JOUBMuV*wuBKf86)Da#FEyD7`r*lB+I6Fi#|7Cn%S zrOb3^KTc%^M^lL-iB}99-oO|PS;%|nk6Z$bgDWm7_!XC{f0j!PbMdoXxo&50x;d(p zS=YJon)!XWMFq%pjdi9JUSHRT+ne3^JCk!zPZDoT;VpW?be5`*N^rR2e={iF-iO`I zbv^jU1@(O}qB$4WfD30rCC-A;oV{n` z9QXy!g;h8Y)+TX2i_Hy2M(kP^S}9cc-FTlHQ$HqaDCiWh<5I|`JL%5`7A<4-aE8S( zris|thw0`X>|J0QYtA?OAG#HSZtU~tn_1Eaw}aD-k7j^vU^N#{?1qVMeEj(JCo_Qj zW?X5(Qv^#lo3Z=Yjjrr8KC7xh3ZIwt<4cn-ZPs}HeXqHA8)WaoS6Jn{@EHJ+Gw?-{+|hQuv|Q%;gh5soMiJ91fp#LwN(Eea$ZTy>h{$JoaX#hz)hr zoTjWaWv9u}4Gy(yg`09y$ZIhmSe!T!GRSG+cmm}Qv$!i zE1Jhp@)!r$ai}~6q;}z6ygS^?bv)1q%Gj%|9%pX~d*%ur!!=(f9_lu_fyk zDDzv6+cDOXZ()N@YA~y<>=0SabtPVN51rg#Q5_Yskvr2gqj49_sY}r*az{g1lG7So zsauuZG)<>2NYm1>=K4KQTA!lRw}U63=+x&c&P+)sk{2-YVLzhjURKmFmCs@GQ8!}>qf%E%nG{AOw>WAJ?Y4@b!tg2*9AEp>W280hGJsfP&}XW z5a&5Jbu!m3KObddChsUkYG9-F8ATcFp69t&xYvAvFDM7N?|6~3&>@|j1~gZr^&V!) zceBh1$7VRXxJgUW*}ZfQH)HfG(~s(li#Gwh#F{5o^Gk=!=AjwoWvfoy#06`BwjofM zO|_cCp_S42&{iDD2k6!B^z1}~1vw!qX|LFbx(T8f3 z)ER%wjD4)P|7Mte&PxjZDgz(Rz~5!y?=$dG1Lo^lMqHDrf0SkHF=c1=*fX#r13Ps% mfF`$TFso!R8*vUl`Hz~=W2&6uWfk$C(`u?R4KdY%_x}Jbb{8}N diff --git a/build/lib3270j/org/lib3270j/Telnet3270Client.class b/build/lib3270j/org/lib3270j/Telnet3270Client.class index 952528329b63cacd9df9cecfa9f0a4b1680f7230..27c975362c8686990a4886ed0d0bdacc72f9936c 100644 GIT binary patch delta 1671 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 diff --git a/build/lib3270j/org/lib3270j/input/InputProcessor.class b/build/lib3270j/org/lib3270j/input/InputProcessor.class index 68143e5407a01ee2fc095cf2f2c56bdd0f2ba39a..0158a88263cdf05831c50f942b4aa9c1d0df4fcd 100644 GIT binary patch delta 1987 zcmZuydr(wm6#t!l-R17eK5zxuMFe$RmPr&wG!cX_W>hQ*GcC(m;SQiH5H2=0M-sPdw-I?!x_nhDPo#*%M zjm)kb`OKXIhXG8*l@__n9Feirj%|2~AT3pDD~oC>Wo*X|2|Mk03!@3nMZsmktWdDF zDyux)P*Yn)u!NM_s&KUc?zZD?yu+f3>R>}rm|*fx%P5v1qsflFc$Y9Jyu4m1W^J${ ztTYHfamE}O@1t452X+%a#7C;jGTzpLk0rF)(S~-l%rf7x-;NG^LNJEv*k!W1&XTNl zS}r@w!@-KhGlTWBg9}57gwNG$Qcc7ji@l_-p=36}t}JN`1;fg;+WJP0$>T4*H;KHA zIfS^y%NJJ8YN%PF5E2CgC)ijKZfsC0OKOx*< zs;p4W!kp|0V;5zGm5^FU@#=%tJR?V}uD0gbhJx#f1H2wok6JV7kouc7E&EeU5E8C_ z3crcp=0F>~&AcHT;?<%F(|A3M&v-qk7RDt;`Zeh+JMzCIsrQnMVCYeugo%g2z&~~n z@U|nheya(>YE#T=xXY{l;dIiW4X&Zm7kxs})>4+7 zib+DQ+yN>4!bQ9RAAZG1{N9^_Po!`;lNEK;x9&=(Ge_dW1T37 zqWv*Ys^t-GgxI1fQGPCo=@Lo;t8jwqIH!8G3T z&ab~Rt0#+%F^VTRR4>OVJ~`4coc{Cy`!X%eA9bq*UO~t#>6$ecX=1+5g`O21n7kPy zgpOtF?^Z^1t;N}`88hI~0)gZjl;oPk&SMf`N7))P^(sqpEgp-#dZo^#D%JuD-D+Ez2@R-F_q&MP4 zq;U=F5Jyk*SS{sY4I`5pkxk1`K+j-0?`P3+JVwuA9<4x-B3MK#(MYSXf>z@tT7wPr z0yffGY^Rqa*h{aVh1Q{sUPA}1$7$MtE2QFQdL1`uBmSmMd~n|&nYL0qZKKh&oyO1( zt=oQB=bJy*jxQ~qo-0sxOb9G5SQ>+nJuXyZkIg0l>u&))-eh2#hftxX% Jj4>E+`9G^IvhM%@ delta 957 zcmZvaTS!xJ9LK-^-Htmua~Iv5&D(Op<`P@O)C$TLy%m%ZL=nt6P`V%{LJ#3{z5XHS zVmC`O?PAkqvn)%aeCgt2ltC9rBCuX6#_IRqPEMo^e&_J}eZIfj`K3bh0eL2!oB*&D zQ^RsfZ#s)}5_%Mz#|3`EP;0t`%MyAOT)|cT+_2x!uizR6ctH9c>^#;U3%5o(BGJxR z2ZPQR_U~aZH+4p$2e(JscSo8J#TcZcu_G;wZ7mY+^4h#MUTtj9iVSVWkf{(9$91xU zyx$mLLwwfg>ArzF;i0BV>B;D+uNr}Sh+Y_kHDsI~vNv&ytU$}4p+Ci{+@o+0FJo5{ zb^E_*%a^CEl!+bA=HLt6wTFeG{t95Nal(Iudb{b|pg$vJ6 zffBYhN$JAr+EmmE}Evj zAmkF$7@tTFX$trxiDNJ5HqxCR1;E@LXDXFw2Dm@z9Fo~(L`T!!BHkX zRQ*ops_w;ouzHwS11xMk3aE%ks0A}onnzij909sQak8c=E#09o<+=T2CSjYPXPfC1 j8c8&{C=|yfeu`aA;LI2<#?hBiI+xtMouJ1AY4GGPO$)u) diff --git a/build/lib3270j/org/lib3270j/screen/ScreenBuffer.class b/build/lib3270j/org/lib3270j/screen/ScreenBuffer.class index f531834bab071dfb28fefc9e4451429e0d17e717..2dbf12c1ed172c3694f0d303f3857cd0f56bc900 100644 GIT binary patch delta 630 zcma)(ze@sf7{;GhPs5WW3nbD8moQtjp&{spUKuxDT0!)akhE;-j2hBGXlM(ka%!@` zMe-;b+UsBFckNBh)vI?&3Q-()y!U;1-p}*A-}B(nfOzw9c?Dn$uVv9;Q(89TsuWM9 z2(3mnlT;H@lwkGAvfMjOuo;a=h+y%_;o8}%E*qCvXw;EVct?%rUAA`$oY}>qBYOct zkzp$^^8*Ck1+=;S6%6wmN*qHdb201g_c-yHIna>Pf6W^9{DOwc+ENJ|)@ zLD7L6HdalXZi=l_6HHCQLu=@vb&j55`bGow5r+nKf}ti}iq~C` z8kcP`mvmSKxoX@}eYs*)K`J^ZU7t!k?+MrWH+$#M_jUc4pfsy@S)FN6KiB5Jd;!aI Bgna-2 delta 36 scmbPbw%w5H)W2Q(7#J8_818K3;^Um0&H0H@dvhGuaz0iD1}28v0Qva~%m4rY diff --git a/j3270/src/main/java/org/pubvm/j3270/J3270App.java b/j3270/src/main/java/org/pubvm/j3270/J3270App.java index 84f255a..d9e3345 100644 --- a/j3270/src/main/java/org/pubvm/j3270/J3270App.java +++ b/j3270/src/main/java/org/pubvm/j3270/J3270App.java @@ -6,6 +6,8 @@ import org.lib3270j.listener.ScreenUpdateListener; import org.pubvm.j3270.ui.ConnectDialog; import org.pubvm.j3270.ui.StatusBar; import org.pubvm.j3270.ui.TerminalPanel; +import org.pubvm.j3270.ft.FileTransfer; +import org.pubvm.j3270.ft.FileTransferDialog; import javax.swing.*; import java.awt.*; @@ -31,6 +33,8 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate private String lastHost = ""; private int lastPort = 23; + private FileTransfer fileTransfer; + public J3270App() { super("j3270 — Java TN3270 Terminal Emulator"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); @@ -125,6 +129,8 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate client.reset(); terminalPanel.repaint(); })); + actionsMenu.addSeparator(); + actionsMenu.add(createMenuItem("File Transfer...", KeyEvent.VK_T, this::showFileTransferDialog)); menuBar.add(actionsMenu); // Help menu @@ -154,6 +160,69 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate return item; } + private void showFileTransferDialog() { + if (client == null) { + JOptionPane.showMessageDialog(this, "Connect to a host before using File Transfer.", + "Not Connected", JOptionPane.WARNING_MESSAGE); + return; + } + + if (fileTransfer == null) { + fileTransfer = new FileTransfer(client, new FileTransfer.FileTransferCallback() { + private ProgressMonitor monitor; + + @Override + public void onTransferStarted() { + monitor = new ProgressMonitor(J3270App.this, "Starting IND$FILE transfer...", + "Waiting for host...", 0, 100); + monitor.setMillisToDecideToPopup(0); + monitor.setMillisToPopup(0); + + // Start a timer to check for manual cancellation + Timer cancelCheckTimer = new Timer(500, e -> { + if (monitor != null && monitor.isCanceled() && fileTransfer != null) { + fileTransfer.cancel(); + ((Timer)e.getSource()).stop(); + } + }); + cancelCheckTimer.start(); + } + + @Override + public void onTransferRunning() { + if (monitor != null) { + monitor.setNote("Transferring data..."); + } + } + + @Override + public void onBytesTransferred(long bytes) { + if (monitor != null) { + monitor.setNote(bytes + " bytes transferred"); + // We don't always know max size in IND$FILE, so we just pulse/update note + } + } + + @Override + public void onTransferComplete(String message) { + if (monitor != null) monitor.close(); + monitor = null; + JOptionPane.showMessageDialog(J3270App.this, message, "Transfer Complete", JOptionPane.INFORMATION_MESSAGE); + } + + @Override + public void onTransferAborted(String error) { + if (monitor != null) monitor.close(); + monitor = null; + JOptionPane.showMessageDialog(J3270App.this, "Transfer Failed: " + error, "Transfer Error", JOptionPane.ERROR_MESSAGE); + } + }); + } + + FileTransferDialog dialog = new FileTransferDialog(this, fileTransfer); + dialog.setVisible(true); + } + // ========== Connection management ========== private void showConnectDialog() { @@ -221,6 +290,10 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate private void disconnect() { if (client != null) { + if (fileTransfer != null) { + fileTransfer.cancel(); + fileTransfer = null; + } client.disconnect(); client = null; terminalPanel.setClient(null); @@ -280,6 +353,9 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate if (client != null) { client.getInputProcessor().setKeyboardLocked(false); } + if (fileTransfer != null) { + fileTransfer.onScreenUpdated(); + } terminalPanel.repaint(); statusBar.updateStatus(); }); diff --git a/lib3270j/src/main/java/org/lib3270j/Telnet3270Client.java b/lib3270j/src/main/java/org/lib3270j/Telnet3270Client.java index 0d1dc31..06d879d 100644 --- a/lib3270j/src/main/java/org/lib3270j/Telnet3270Client.java +++ b/lib3270j/src/main/java/org/lib3270j/Telnet3270Client.java @@ -115,6 +115,9 @@ public class Telnet3270Client { /** Get the input processor for keyboard operations. */ public InputProcessor getInputProcessor() { return inputProcessor; } + /** Get the data stream processor. */ + public DataStreamProcessor getDataStreamProcessor() { return dsProcessor; } + /** Get the connection config. */ public ConnectionConfig getConfig() { return config; } @@ -130,6 +133,11 @@ public class Telnet3270Client { } } + /** Emulate input of a string, pressing Enter on newlines. */ + public void emulateInput(String s) { + inputProcessor.emulateInput(s); + } + /** Send Enter key. */ public void sendEnter() { inputProcessor.sendAid(org.lib3270j.protocol.DS3270Constants.AID_ENTER); diff --git a/lib3270j/src/main/java/org/lib3270j/datastream/DataStreamProcessor.java b/lib3270j/src/main/java/org/lib3270j/datastream/DataStreamProcessor.java index eac671a..b2ecec3 100644 --- a/lib3270j/src/main/java/org/lib3270j/datastream/DataStreamProcessor.java +++ b/lib3270j/src/main/java/org/lib3270j/datastream/DataStreamProcessor.java @@ -30,6 +30,9 @@ public class DataStreamProcessor { // Callback for sending data back to host private OutputSender outputSender; + + // File Transfer DFT handler + private org.lib3270j.ft.FTDft ftDft; /** Functional interface for sending output back through the telnet stack. */ @FunctionalInterface @@ -48,6 +51,10 @@ public class DataStreamProcessor { public void setOutputSender(OutputSender sender) { this.outputSender = sender; } + + public void setFTDft(org.lib3270j.ft.FTDft ftDft) { + this.ftDft = ftDft; + } public void addScreenUpdateListener(ScreenUpdateListener l) { screenListeners.add(l); @@ -698,6 +705,13 @@ public class DataStreamProcessor { processRecord(data, pos + 4, fieldLen - 4, false); } break; + case SF_TRANSFER_DATA: + if (ftDft != null) { + ftDft.processStructuredField(data, pos, fieldLen); + } else { + log.fine("SF_TRANSFER_DATA received but ftDft is null"); + } + break; default: log.fine("Unknown SF id: " + String.format("0x%02x", sfId)); break; diff --git a/lib3270j/src/main/java/org/lib3270j/datastream/QueryReplyBuilder.java b/lib3270j/src/main/java/org/lib3270j/datastream/QueryReplyBuilder.java index 4498b93..74c63b9 100644 --- a/lib3270j/src/main/java/org/lib3270j/datastream/QueryReplyBuilder.java +++ b/lib3270j/src/main/java/org/lib3270j/datastream/QueryReplyBuilder.java @@ -31,6 +31,7 @@ public class QueryReplyBuilder { QR_COLOR, // 0x86 QR_HIGHLIGHTING, // 0x87 QR_REPLY_MODES, // 0x88 + QR_DDM, // 0x95 - Distributed Data Management (file transfer) QR_IMP_PART, // 0xa6 }; @@ -68,6 +69,10 @@ public class QueryReplyBuilder { // Reply Modes 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)); @@ -189,6 +194,19 @@ public class QueryReplyBuilder { return new byte[] { SF_SRM_FIELD, SF_SRM_XFIELD, SF_SRM_CHAR }; } + private byte[] buildDdm(int bufferSize) { + ByteArrayOutputStream out = new ByteArrayOutputStream(8); + out.write(0x00); // reserved + out.write(0x00); // reserved + out.write((bufferSize >> 8) & 0xFF); // inbound length limit (INLIM) + out.write(bufferSize & 0xFF); + out.write((bufferSize >> 8) & 0xFF); // outbound length limit (OUTLIM) + out.write(bufferSize & 0xFF); + out.write(0x01); // NSS = 01 + out.write(0x01); // DDMSS = 01 + return out.toByteArray(); + } + private byte[] buildImplicitPartition(int maxCols, int maxRows) { ByteArrayOutputStream out = new ByteArrayOutputStream(22); // Implicit partition sizes, 2 self-defining parameters diff --git a/lib3270j/src/main/java/org/lib3270j/input/InputProcessor.java b/lib3270j/src/main/java/org/lib3270j/input/InputProcessor.java index 99d9ea6..0d13427 100644 --- a/lib3270j/src/main/java/org/lib3270j/input/InputProcessor.java +++ b/lib3270j/src/main/java/org/lib3270j/input/InputProcessor.java @@ -319,4 +319,113 @@ public class InputProcessor { keyboardLocked = false; insertMode = false; } + + // ========== File Transfer Support ========== + + /** + * Send an AID code, bypassing the keyboard lock. + * Used internally by the file transfer protocol to send Enter/PF keys + * during a transfer when the keyboard is normally locked. + */ + public void sendAidForFT(int aidCode) { + boolean wasLocked = keyboardLocked; + keyboardLocked = false; + sendAid(aidCode); + keyboardLocked = wasLocked; + } + + /** + * Type a string into the current cursor position and press Enter. + * Used by IND$FILE to inject the transfer command. + * Equivalent to emulate_input() in x3270. + */ + public void emulateInput(String text) { + // Type each character + for (int i = 0; i < text.length(); i++) { + char ch = text.charAt(i); + if (ch == '\n') { + // Newline means press Enter + sendAid(AID_ENTER); + return; + } + typeCharacter(ch); + } + } + + /** + * Erase the current field and return its capacity. + * Used by IND$FILE to prepare the field for the transfer command. + * Equivalent to kybd_prime() in x3270. + * + * Returns: + * - The number of characters that can fit in the field, or + * - -1 if the keyboard is locked + * - -2 if not in 3270 mode + * - -3 if no input field found + */ + public int kybdPrime() { + if (keyboardLocked) return -1; + + if (!screen.isFormatted()) { + // Unformatted screen — use entire buffer + int size = screen.getRows() * screen.getCols(); + screen.setCursorAddress(0); + for (int i = 0; i < size; i++) { + screen.getCell(i).ec = 0; + screen.getCell(i).ucs4 = 0; + } + screen.markAllChanged(); + return size; + } + + // Find the field at cursor and erase it + int curAddr = screen.getCursorAddress(); + byte faVal = screen.getFieldAttributeAt(curAddr); + if (faIsProtected(faVal & 0xFF)) { + // Try to find first unprotected field + curAddr = screen.findNextUnprotected(curAddr); + faVal = screen.getFieldAttributeAt(curAddr); + if (faIsProtected(faVal & 0xFF)) { + return -3; // No unprotected field + } + } + + // Move to start of field + int faAddr = screen.findFieldAttribute(curAddr); + int fieldStart = screen.incrementAddress(faAddr); + screen.setCursorAddress(fieldStart); + + // Count field length and erase + int fieldLen = 0; + int pos = fieldStart; + int size = screen.getRows() * screen.getCols(); + while (!screen.getCell(pos).isFieldAttribute()) { + screen.getCell(pos).ec = 0; + screen.getCell(pos).ucs4 = 0; + fieldLen++; + pos = screen.incrementAddress(pos); + if (pos == fieldStart) break; + } + + // Set MDT + if (faAddr >= 0) { + screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY); + } + screen.markAllChanged(); + return fieldLen; + } + + /** + * Send a structured field response directly. + * Used by DFT mode file transfer. + */ + public void sendStructuredFieldData(byte[] data) { + fsm.send3270Data(data); + } + + /** Get a reference to the TelnetFSM for direct SF operations. */ + public TelnetFSM getTelnetFSM() { + return fsm; + } } + diff --git a/lib3270j/src/main/java/org/lib3270j/screen/ScreenBuffer.java b/lib3270j/src/main/java/org/lib3270j/screen/ScreenBuffer.java index e1cec28..658a14f 100644 --- a/lib3270j/src/main/java/org/lib3270j/screen/ScreenBuffer.java +++ b/lib3270j/src/main/java/org/lib3270j/screen/ScreenBuffer.java @@ -326,4 +326,59 @@ public class ScreenBuffer { public ExtendedAttribute getDefaultFieldAttribute() { return defaultFA; } + + // ========== Low-level cell access for file transfer (CUT mode) ========== + + /** + * Write an EBCDIC byte to a cell at the given buffer address. + * Used by CUT mode file transfer to place data into the screen buffer. + */ + public void setCell(int baddr, int ebcdicByte) { + if (baddr < 0 || baddr >= maxRows * maxCols) return; + buffer[baddr].ec = (byte) (ebcdicByte & 0xFF); + buffer[baddr].ucs4 = 0; // will be set on display refresh + screenChanged = true; + } + + /** + * Write an EBCDIC byte with extended attribute flags (cs=0). + * Equivalent to ctlr_add() in x3270. + */ + public void setCellWithCS(int baddr, int ebcdicByte, int cs) { + if (baddr < 0 || baddr >= maxRows * maxCols) return; + buffer[baddr].ec = (byte) (ebcdicByte & 0xFF); + buffer[baddr].cs = (byte) (cs & 0xFF); + buffer[baddr].ucs4 = 0; + screenChanged = true; + } + + /** + * Modify the field attribute byte at a given buffer address. + * Used by CUT mode to hide data fields during transfer. + */ + public void setCellFA(int baddr, byte fa) { + if (baddr < 0 || baddr >= maxRows * maxCols) return; + buffer[baddr].fa = fa; + buffer[baddr].ic = 1; // mark as field attribute + formatted = true; + screenChanged = true; + } + + /** + * Get just the EBCDIC code byte from a cell. + * Convenience method for file transfer data extraction. + */ + public int getCellEC(int baddr) { + if (baddr < 0 || baddr >= maxRows * maxCols) return 0; + return buffer[baddr].ec & 0xFF; + } + + /** + * Get the field attribute byte at a cell (the fa byte, not the ic field). + * For CUT mode checking of field attribute modifications. + */ + public byte getCellFAByte(int baddr) { + if (baddr < 0 || baddr >= maxRows * maxCols) return 0; + return buffer[baddr].fa; + } }