From 39c6e9a02563fd2ea5ecce0962d1878b82e28e16 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Sun, 26 Jul 2026 23:35:39 +1000 Subject: [PATCH] build outputs: punk::zip 0.2.0 into bootsupport + _vfscommon, thin-layout make.tcl sync Regenerated by 'make.tcl modules' + 'bootsupport' + 'vfscommonupdate' for the G-124 change. make.tcl loads punk::zip from bootsupport, so the reader only reaches the zipfs-less bake path once the snapshot carries it. Also carries two version propagations pending from earlier work, not authored here: punk 0.2.7 -> 0.2.8 and punk::platform 0.1.0 -> 0.2.0 (the latter was added to include_modules.config in 5c1e6927 but never snapshotted). Their source buildversion files are unchanged by this change-set. The templates modpod is rebuilt because the layout make.tcl inside it changed. Note for next time: 'make.tcl bootsupport' cannot replace a bootsupport modpod that the running make.tcl has itself mounted - it failed twice with 'BOOTSUPPORT module update FAILED: ... templates-0.2.0.tm (invalid argument)' while a plain tclsh copied the identical file without complaint. Copied by hand, then re-run to reconcile the punkcheck record. Assisted-by: harness=claude; primary-model=claude-opus-5[1m]; api-location=anthropic.com --- .../modules/{punk-0.2.7.tm => punk-0.2.8.tm} | 11 +- .../modules/punk/mix/templates-0.2.0.tm | Bin 287169 -> 295621 bytes .../modules/punk/platform-0.2.0.tm} | 83 +- src/bootsupport/modules/punk/zip-0.1.1.tm | 914 --------- src/bootsupport/modules/punk/zip-0.2.0.tm | 1698 +++++++++++++++++ .../vendor/punk/project-0.1/src/make.tcl | 25 +- .../vendor/punk/basic/src/make.tcl | 25 +- .../vendor/punk/project-0.1/src/make.tcl | 25 +- .../modules/{punk-0.2.7.tm => punk-0.2.8.tm} | 11 +- .../modules/punk/mix/templates-0.2.0.tm | Bin 270581 -> 295621 bytes .../modules/punk/platform-0.2.0.tm} | 83 +- .../_vfscommon.vfs/modules/punk/zip-0.1.1.tm | 914 --------- .../_vfscommon.vfs/modules/punk/zip-0.2.0.tm | 1698 +++++++++++++++++ 13 files changed, 3594 insertions(+), 1893 deletions(-) rename src/bootsupport/modules/{punk-0.2.7.tm => punk-0.2.8.tm} (99%) rename src/{vfs/_vfscommon.vfs/modules/punk/platform-0.1.0.tm => bootsupport/modules/punk/platform-0.2.0.tm} (73%) delete mode 100644 src/bootsupport/modules/punk/zip-0.1.1.tm create mode 100644 src/bootsupport/modules/punk/zip-0.2.0.tm rename src/vfs/_vfscommon.vfs/modules/{punk-0.2.7.tm => punk-0.2.8.tm} (99%) rename src/{bootsupport/modules/punk/platform-0.1.0.tm => vfs/_vfscommon.vfs/modules/punk/platform-0.2.0.tm} (73%) delete mode 100644 src/vfs/_vfscommon.vfs/modules/punk/zip-0.1.1.tm create mode 100644 src/vfs/_vfscommon.vfs/modules/punk/zip-0.2.0.tm diff --git a/src/bootsupport/modules/punk-0.2.7.tm b/src/bootsupport/modules/punk-0.2.8.tm similarity index 99% rename from src/bootsupport/modules/punk-0.2.7.tm rename to src/bootsupport/modules/punk-0.2.8.tm index aac58c5f..ce6dd983 100644 --- a/src/bootsupport/modules/punk-0.2.7.tm +++ b/src/bootsupport/modules/punk-0.2.8.tm @@ -9002,7 +9002,14 @@ namespace eval punk { runtime-artifact metadata target field. The raw Tcl platform-package identifiers are shown for comparison (punk::platform::normalize folds their version-dependent - aliases - amd64/aarch64/macos - into the canonical names)." + aliases - amd64/aarch64/macos - into the canonical names, and + folds the msys2 family - mingw64/mingw32/ucrt64/clang* - onto + the one 'msys' tag, since msys2's uname renders that token from + MSYSTEM rather than from the binary). + These are HOST names. What a name implies for build OUTPUT + (.exe suffixing, process tooling, which bin/runtime tier holds + a runtime) is a TARGET question src/make.tcl derives from the + name - a cygwin-family host targets win32-x86_64 by default." @values -min 0 -max 0 } proc platforms {context args} { @@ -9505,7 +9512,7 @@ punkcheck::cli set_alias punkcheck package provide punk [namespace eval punk { #FUNCTL variable version - set version 0.2.7 + set version 0.2.8 }] diff --git a/src/bootsupport/modules/punk/mix/templates-0.2.0.tm b/src/bootsupport/modules/punk/mix/templates-0.2.0.tm index 42e474146a36f713d210d99ef602e0638d7f3f7c..5b144e73b58d1b75d6399db6efd9d2293fedfc79 100644 GIT binary patch delta 88270 zcmV(?K-a&)!x6=W60j8-0#mkw7aF%08UX`U5y%qyTquVVy4D2%0AdpW0CKgl10BOPY>CaF`IeVJZ~+?ucb8mn0WSfM zmyB=$Q4&$Q4dr3^DMjpq(i(t;1TYtmm+x=^Mj zPIa9WC^{h1YXqK-WtYY>!F_wh*fVa)RiN-YiF{0ElU~xGZcHkS93s-}m-+johZ6Cq z>{+VwUYWOtmLRw@_6MqX+n;=4ks$JOYM{T=b}>z9R?=56vXb*q>w|jk!DQB;P{_o8 z&F91i1^L-oZmY*yTk?Jl-|vE=Uxpd;AX=pOjfrX$$i5CP4{*DHHG+@ua!7lCnob>!5m6-Zs*~r@zuMlwXU% zu=5xee1I|=A|4zuG!M21^N0#Wlht&@VCynDA!I;f;Dn0ZIk}WiJ^T0W&8Pdj`-g}B z*w1^7@j$5M68%+Mm>nj@2O3Wp+nCgc0Du1b`k&h`^G;k%XtwPe*!J`tdL-O`PYB%?zGmJGbX zBr5W)=&dMmF~e4v_%wmaA^`*3oL~dG6B1f2PDo%?p+c!Rz}l3(SM ztaHpd9}CD;)uM+qHg)3t!5R6(94zIW+3C28xk zyL=mVR^-TrKcYN+k!A9%EENwR2XwOomv48)0sX~oHit{i zw^Hle9!-p+_!2b-7w!E89x(@p8Xi1eCkLL`qkgfk3{-^QlXpMu2tR&M+uctdCX@dG zP)h>@6aWAS2mlqAh;;!GlQJMAmr!p3KbQP<0Unp&4Fd(Y9d-d1D-RR3`dmOXMT*-? z0RY@@1(PozCztSQ0W6o6p#c$p{dOBik}vo_pCTK?9S|}LBqfihP1({_+iJJYxFsK3 zJ$>($FuVnzNLCZ5LRSH#XqtQWA@&LPNj82Fky)Ab0f4gHJ3Bi%wgjNE@*^@bGQMNq ztfuq5{k?s&TxVC4JYSi}EaI!g#Mv#LU8Sp%KF`b|pRMOfY0}JmwJh>~za`VvEG?cL zmBrMY=kr-oNW0}co+f5>nJoBgT$cGXjaSLc%;HsS&Qt2-sGJt*a>bodOS6oNBwJ}W zCO1c018C{^oV3KA7?rflw7*r#!Oc^f{Gf%FQISpn8^s0SKU6|<8{h{98CdDGn z;&~L`(3H(IpXcl1=*8=QpI(_|K2N8&=HR>N_IGW0Ad^;oyYSsIm+^*{QTUcG;onHkZJF;d=*ibhh|Aw zluYyTwp<(>H10*v`+c z+DtFwEKBC6Cc=iKKhunt7?#FJN+9^b+v(i=e)KP&9evq&t2}gIPKzQh((HoI1`U{I zXK8tsX7oxtU8UEFiPDT9Pp2V4-;77A;i1V3!aEt`3!1Zk?T}_O%Wq10rn8R-?eZB$ zD3WDftng{D&Qc)7z#Obs=h4X*)ZfizI=z&|(^PDC=-hTmur2d>Vi#d%z=Z>BUwOnw z-CWQf^EzZR?8rkt1K8%HA|XOT7)P6^^E;4DIge8U3vDZ%=VD6dAfC-q05qP{jF#*2 zlKPJYiK(}LRYAB^KBuwI_muZ&p{MQRm63mlm7bsHl zVMX21u#0#_Fjp`ni+1AE#t|Y8c^pe>=<=6OXlf>wr`cp zafVhjPU)YypT7Iip1VVj+(uMiKKooDD-_Wv&BHD3j zod81x7aSLC1;L;9ESK(bLM?goVjWZCRYD#9oD?}AW+u0*q#TaSTM%l3IePiw<&Wl@ zKfQSU`sMdL+wv-1g8bi7cY;09bWWt-;RT&x^b-l4<{hJH7R4I3fV@Go#c**^5QbA{ zCG~ZG@&|K#{FIhS@Q+<*ID8qSp`zcRNk_?uS`aOmyOSk=yh;+98r@>l9S9ARX1Z7g zxaCH4!EVqFKV5en<8G87Bz7gfV$NwGjwo1m`kQV1~+K5q%Vm_TnMm) zv8N1s;It8Z2W3bxEk?T|Cg^2+o&ej00v=6&xy;vm9bU!+u?0;DSV?<}>#v&n=_yU-OePb>(lO7d$KVxE08<IMwUpZ*Dv7Oe*cAh`@{@TM%!O5!QX+o z@fKq4_HZ*0hNS?Q@t4)>?cI03DG(EaAf4?0wMbV=`Rs}JNIP>ZxUc|r<{XlejtSvo z^7l2^DY}2Yp8+&_tGrN(ySm9E8n0k~D&#sN z!uKb7N=P7mRil*4Io$y9w8)9h-C)i%U0PLXuG5(Cf^aXg^Pdt`yET(FQTXC1jV67q zGdl@fecG|POy)C?jPp4WTe?Jvl7W?2r3*&wS&@TdGY6?~A=(!$H(xC03C)6m;<}6E z4M2?Ec=eM9!E`ko)%K0&`EHhf((#xwV>J#N6v>$z=kEQ7lgYvJ)A#S*9YNlf>549rgFycyL7))3kpfM_Pgbqqp%Dt8q4 zG1nnowJS#3JR`Y;xOrN$pa-$UAW^nw*Cjm#H;>B+9OOA&21I0BhafuTDy0)PNiaE} z_bMU&*#}ZO`(WN-zo-F!&j1^17&hYo`hB!~_O&@~1N90QqV0x``9&gK7=A0sX5-)$ z^!K}WMRJjRSQ2eBy?^iDdAok!Gkx^f*j2YjjUAE~8jT$qw9W09XBVA>bGvWZY?^7g zp3pTwu%zks{R1R@-QDvz-Wzn~XYz2b>WohZ(?Lwz+~rH7^N)*vi*koUqIWSSkuPh| zNi*WBh0Zj=rW1NMr_77qpIu)ECT%+H;n*9on@Su-ghGX+j|7809FD6UP-LYW-+x+px_dmS^NBjDX zfuv2`>3!Srm06wyzfWkgSB+KLH-vA5L<@fA9i8I>M|v6qJDKo>eY9tBil-?H@Rt4uHG zTBaEgH$gzK;~#l~x5AXDXGDDsPpgjT&QrIYmyv_~wWLbfJq zTZ;T8;<(q7@nm%s(5-y_)a)c?6FZ)kL64K1xFA{?u|BNNtIGh8X*~Hy)OvK6fCrBz zbWW(>phL)i6d1eWW=G`0}0S7wMEZZQ{wwBQQy&$d3>ZNZNWz1OTiU8cf64JwAT? zMRfcx(c^!nTa<($><$>(-+yt0e`pV!@JQU;QWX(@%4}H_fdr;f@&Wa*{&4iypT0j@ z#8YD6zBLQ#E(1+-}Xi7TaiP@NY$jRu{&4wj+^?T z>f-?6YcGTd!!M8QIs(YycqnPQfkI(qej~~>xNAx?cA*-Ao1|7Ew zicW1{?&>0@Q?{Ml2h3@gkgY=VUI>6bUt^zS9>=@QL!|inGy-t`Bd4>{^CWZm#QVVr z@bMS(wz+%Sj~Z@2pc7wd(uTzQ(1%92ft-%i)6-FxS$^XzBI}H3m=H#2hoE2*r45UJ z=FpOHm+lCtk}^;x&+_SdK_r6p$e3pYG8t9UyR+#qX_6i-iFr!2$aPM)hiE>8DC(gW z@s8LAnI|;laxE+U+5e%^U!0nF+wJY&*md2nl~woKEC-HLd8l4ix+K2+nhja3vV8Me zfG#bbsyf_L8`m|&We!oe(h+l?yh}xYOg*voFawm>Mxq+)WHxHoV4bl|V&-e|q{oWy zo37Suy?|1g)i`{65|=<=T~A0fT(A)p%t-QqTC?eet_5Wt>L}b8nx!m zvEs!U?f{1$fN1?3w%tg5yyo3U5F;$Fsw|xRGq>L-$$guA&i%59fxB#7=jo|`lYtxW z&jC_`-bMhS>n-vaZ&{nBS-DR=O_iz|GJtBR+2(C-VyG&uMp`<&NHR76&4@hA65*Jy z(iI(bP!6kMUQr7=372_>uV~XyT#xn|MAZ_|YEHBbP`&zK);oR&J@pQ^H)PO1ym|AM zY0O`KJp1^37*GRW5|S-vCs zn_bX;^*5o#*gQAy{+6fN=4R%zu{otXb=-n0+sD&+(RE} ze322h;!a@GSGtN=WB@*9bT*;Q{*g5jLi8~^D?PFGm}ZqMz+3Md)q1g2M)wTbXNB{~ zyhOXBoK2H6cyPoEl0tDh&@=dILR3~-`2fY4xVxjj^##IEi!v>Uwt26u24iz8A0tCe zkl?rdjkVJ6`qkX-7cg9ZfZ$%n<>egK;@-a5$A6#f|K1)SlD-{5zV24#>mvSJUR?91G^0;@dxCZ5 zg)vX}|MJSb0Kzf(CVCg&?(yGz^}H~5_j|jWfyB*6-Fn{PCMW)XyI6=;O}@|40^R9k z<%RLMW_6Grq#eJ-O5?uYeeI3q)x}u>Ijcy(M3s6kJ`G#&!6YQthru(Y=iYtl9z&)- zJDGb~9O_lwQFdVwHPnPdBU+NOe;NC+U8b`cbSDMgA643T>G_PNL$jc{5obXVEU4+7Jo0G|jt`K( z<23YrG_DLC+$t@_VIYU1x#|O#Z30)J>UQPLI6dv27`Gppp9nSPDM3?bGtl7&mCeWv z*$X8|2i$jmpc*EEK)n#tb42ghg_sCuv^0;Z73~XVD5QZJA_@#G4VKB0M6hC)7Ur@Z z9h!Jf9Ps+$l1>JlfmxmnRyC;FWy+B6Nb-vd@T6>XNi(M15th|=vmbdAa|Vt@yfo)R zTk4(O@lFVbpeyWiQ&mXcZ$Jy{toR=dE9|`fkw|fWyQH0+{TFt4yTA(1|8Tg$v-*c) z#`%BI53^dF>D^4CI$gHu%-rb@xUmA8)$1ggvGeVOxGlQ6m0c>>{fhzcb~-27ap2T% z06$%+gBse1a41Yky{+kc!v|l+4Mm;8;$*WXPFo$fir#e~Fu&?RP&aV??p+JK>>L}@ zQ|y0#l}S6x0}bp89!AqN;64Vcps-}Fb+%qilA_mQ?PrTlr>%}KW@hW8-DDe zwJ>qmwD@QO{)T&?zAnJJhFVmW%2Ki0UErMHCt}t5ZrrXtsamwm-0xk+kNqyqv!?e( znRgU47dw9~@fUqRo z=UF->HW^48oY#0dP2lnxTQABG57iPT2sp{1D4H3bt^HCilj&~w--Y|F6Acc6I|R}h zV`fDZq-7WOV>3!0;|beCYMb0>n@Y0sb&$E~%kvgjp@MmQ=aGZ&^W>by$^Fd_4k9go zkUg2*$q{!F+g1HF^^sxKE4#`BoitUJy$sES<0m>c ze>@Q5Pe#lGdoC0GVtW;VblBOOK!SEOCduA3?g|TtR`T7381u`r3lEP?AA>|EWAk)B zF3Y5l<_MTz{Uob5nOO49vAZ@K;)%BdE5vbSv|SVGxTfxJn@Z6=6`sgvdNAC7WQt9b zdC+hb4a2V;CoedGXt(v`)rULY*z$hk(;KiQXY$Xagroz-GIM8D7j(hn+(BN zY&jw_M!!rp4!wzql4lXmfa83B;N+EjSaNJzZ|`Anyx(%GwL2RNmn>qYgZyZdh^bog zS&eJOGw_-r_c}*p9j(sHlbC%HnJrS|{H+S$81#AQR{1FA7@|kPdmR4DL9yo&^O9Rr z(h5;d(AUz>x2ymLHwxQEdQLOUR-Pvv?zofD@d<8c&#{WGwUf~aAF^YAdVG9RTgA!b ziTV0#`iEGmCwr};2`{wo!D8%AkTWFR!(_7Nr|hI=zUCF_O9A6c*s&-t8k(2QTM-eA zLJ>iSB6&UF!kaQxRGa%(O#-{0k@yprA*tJ+=lPXJ|Hg&{BP)8?862dEC%;%L0(1@9 z-OxzPZNb|>Jw%K6gK_GA^?*kQ>iF|)arUp-8Um0xfd?J6GGTgP`wD(1!O>WQO&;Na zU)-iCh}6gvxEB%fx^*IMx$jzuhpeE}dA)zf14L@o?yC{+vlDBJ-bz5b08ws&L~C#R zyr-a*y0_s|-VsJmK7MYAa3E#;5&t2d6hQL_(69ukijPAM;aYQlnSz}PX z$8GZx5if80j(j8lR{2K3=mB8Zd|9%fnRfw&X3_eV(~A+%qJC|gw=yHa+uJVdPkJcZ z`H+^|!)JeNB&|$;8~l!cGAr^f#{~z>1-i#-HxwdI-ITfYToHzuBglL0epCoQz}*bx zJ>qT6`hs;_xZZCPQOBE`Sg!0@&ex~YR_mGu2CCRt{L15%3|g+Jg^VG-?bi5APdRr& zECG_vX$I{5bbdz2TExYz`d}8xb(-8%oRKvzoUyxBIyHBH0S9CspPt(1#DiuWZ2*s% zIK8;k18Q4Z#hL3voh^J<{k>3yM!!~^eol-NtUopoDsb`7%V7`{#TJ+4+SHm^NB_M> zFqjx+h*iK_p6O|weg3FL1BX^6(U57IRySJ(F7Ijl@^JLU| zZ8xUa#egrDQ*Xt+AFy?gdL4^i;_0#tl)boEbiNjQY?KH6_*Vl;1vZRv_T=%_wcc;Q zCPv|^ckA=JZGKh63e68O4Ig@4oD`2B;W4{WTK5uvz0tWkH!T1G%znI>eerp(9i_g_ zSu7FrjRozY^dEho?T|bbufBis_y_uC7uStqg6>-ZPlq#aQ;+lN%6uLdX}Q;>!(1>! z+XFWD_|%WP`G8K;`F+==zF{R@m!fbJwb>CeKiV+8{TdPawbS-6fBURkc4QV8wCi14 zLew{ZFKaxq`6e%5FsG|9Me?5X{8l{IX$tIeS*){+vmC%y^u_0sntFQQDQ(&B-lntR z3pzGW9#7b-z3^kX8o13k@c-WKx4skt#n9A$Ot<{wS=3>+^)8o60>&%%QU8vXb>FbR zI>ya;$ee*n=0Rxu{;SvDzk2=BeDmgq|A~8FT!$;BE_fG_rIo+Q8QMxX>!|bMt8;Dr zoP@0qAWRMFJ7N_|LMp=jLGU6nR?$Ca*(!PzzCgd$n$!3j-Kgr9HZ4i8a z{o-@^+THDXTCBJ)`LM0|bRK6HJPZEBZ}KPNUaBwEEVNbdm78uIh*^LNFadPr;Ra19 z?N28j@qUNge0`m>#KDFvk}kxiKG&R*R=q zmjiY_b{LWq>G$%|OC$7H6w&8Bka zu(Ty}^aA0Tdy1aPeQ;=EcvayC-q}OJ_UM@)y?*@w{?@jrdXfm<^LUZcjd`Frd!(p* zBsh7*So3HIODEbNns5H+cYl5L`t0>v&gRF+FzWVJcvs)Iv5A_Zl@r9|-5^1K3EJRr zivs@LT&+TCzeZaar=rFS@7jqMs7G7+j!;%(dJjMY@xv=PJSqzQsu2UZ)`kRDK0$o* zer5T zoRv55e&sA+;ML#$>D5mV%gf7uo`mK7>CI1X_0WvW%j=}L71e?UTZ+THdHLs8Z-0{D z!ZJbV$SN^^UneE&Iwd>W@#A+d-=1pd{v^$gG^qdRt5up5&$LTU%D_1adQM3^GDSl> zE%PFADIIPuU8JQRAU%Yq5N%qM6fgu0n*>DalRO1(O6>w4(ocREcnag6CCLMZhyG?V-9cAD?~$_ z`x?aNHOTI{71)UY^Y^Eg%!5etr(w(83(n1csk=mnN`80JqtFq5x}oy^s;Tj|Oh6F) zvB9Fcg`lg#6ZCOc5cPVZwPagbByVw~jhNrM0mN6pKMX>>-UiTg+=n)dHZRi;(UJ%) z(zd3nTT9YPqk**^r8tml{zyqp+rrcGOj$l<23nzD=cXZ`K3+L^jKRU&@sUuA+ z|Eo+Y(s3MDbxJ#bw2~-b!e>s_!pxNrC)?UBn5fLiYQmFSQ(l5J@s%UlWi@p&-zBGp zh|_evlBAPp$ka-F@j)7ua4Tpnjp3FShSjeJ4PwSAeKqB!Plrtz-HYTcg}anh~PMvtb*ArOBBS zp0SKJ5KFjs=PBs>@3m&nVBR&LAK^RQdLp0kYvU^|kq~;1*EW!gU{HVOJ(fthAn-*t z;)KbiAb~)C>=eZD%}*xS65=6X#fs;eUt>WaO(U?q!|KYvN|x9XPOLf*3TqR_TaiBy z#oBg_CwRsayp#Df_A1l^$vDv?PEUyTT~7KnWnmM3^12VXie@mSU{ICobL4caxJ6;; zkN{e6vdRq@u8cZ6)0ewewt+fqSzHg~Yy)xxf!X_i0YmFpS>eUX7B1Eqasu?K3H8$3 zMryWok)7vEm!a*u*U6Wnztk6|F1YY6a*7qW<5+K;CJP}6Ou(cnJ+jhCb%v^#e_}US zxhzo#z?efZm&DepD_Glx@)!-^qn8%laI3hDR%b)I&+LZR>k19g?4Eb;-oHOOef0c& z@2h8jzYwgq!o8<~J54@Iz5Gg!V_01t-Ox2bCt!w@BS@){6vNKhST?DJpJJYgvk+AK zmiY2=_bfB>RbD=ez9MS*tZl{(RnDAqB2;ReU5*in-22fd{j;~vISFiNISC&G9bg@% z3ufm3{y+aOqM`HisB)})JDqb9f0nwfWq+1`EK5xm&caM)Yk^2+3)W6a9(8pz@ZT0{ z?-DW1G(9+VcYw4ZbKxq5jI(bF@jvU5h&4MMqzDIGnJ|&n5vS|mq$8Lb5_NxK$RSY( zyBDdc$9m4ud{@N(C94}as1jlzJMe)Qz&~WxFH3IBfrQYbj0MS&;NnO^?y)Oa?~p!! zAe}>x6M6KOxOaMh@HdiyifPF;a_QZoFSw1EXBdk=A!YUduE+iTZ}B8-N#nsl^`>|> zSud7nk{#;K_Nk-zIZgAWJ4UvZI;V1IQ1#T#P@mFOfMjEp$dWH{_MTAR*3X!_)?81UiTM$TinQafVyn~&(6 z+TiS<{)+T3u!a6*ObOf3l&oK4rtLDX9TR%n;2Cu*A3eWVLCO1|HFqPe@zHP1cpIi; z5&UHeCWQ{Xyp5@=tYFntoQ$eiu!m42iW9FO&+(JvVO6sM)mKja3M%j=5;RVKW;P*f zOL-PtwJ}OjfPpw)&#A+|uM^_(k^8Z#|U0jHtNxSI3__Ie_dY>}!XTVEz~d9a;Ge=9@%HGogP`TjP8gYQ zk8UBkL&HbYFO#fHv1}6Dk4+8)x=G6fxcnCtJf%dCy@lfmZe>;^LaR`Jat>9)SD#fN z+juYbv`6UH9=r4AMPx4|(DIS7k~rb(>)af?#2&!AeZn^q&}2Gg*9Afqd5CiqpC`Lq z7qa*>S}URZoqV$ZwQ0j}NB8AX^?O99mfUbazBcLopsT?-if^Br1A^|*2n!@B!KJ02 zfST}1GOfL9!yKWS5a7Lk!X%k9Q4Y?TOX!X3^Kv90F_99t0o48jR^q5HOmTaESUmyrJWxY~%LI)F zM=rM#$zQX4O5IH_^AyRXk^Di+L)kohs6@j2s^xYCq=%XYlk;e%l7L~sMQ0={#{5<< z51Di%k9lihRUo4L_;W&%g;yAePS_2q4yBpU(JY}eO&w0)z?;I05oTSHhXD_|oeRiu zb2=U)uIK0fe#$q0iIFx4FV{q+%$6q~h@`81OEoVlN*-!{$H;h0l@`%6VWO+)AY9REW1D}j}_ zSTjdUMLGMUa{~ndj)kp2-V|=6Dn8ZtnTEs*1y(pb82$V-iuVSsETKS{bliA}v+cmnS@0`Pgy5*BWOv0TV zO1qP!;8s6>bf8NsZH2hHFA`wzR9GwO+!b%+RWg)~lwP{x%51BtI°{avQmc($l$ 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 I#Th^)Wli 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


ml26eQK3^#=Ggtokn`)0k1~d9= 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 delta 79696 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

X2J|T>;QHefY`Gz#3Qv$10E2s4qUrS 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 diff --git a/src/vfs/_vfscommon.vfs/modules/punk/platform-0.1.0.tm b/src/bootsupport/modules/punk/platform-0.2.0.tm similarity index 73% rename from src/vfs/_vfscommon.vfs/modules/punk/platform-0.1.0.tm rename to src/bootsupport/modules/punk/platform-0.2.0.tm index 41b41736..b74ad186 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/platform-0.1.0.tm +++ b/src/bootsupport/modules/punk/platform-0.2.0.tm @@ -7,7 +7,7 @@ # (C) 2026 # # @@ Meta Begin -# Application punk::platform 0.1.0 +# Application punk::platform 0.2.0 # Meta platform tcl # Meta license BSD # @@ Meta End @@ -40,6 +40,29 @@ namespace eval punk::platform { # the darwin 'arm64' machine name to 'arm'; Apple silicon is 64-bit only, # so the arm token on macosx always means arm64. Elsewhere 'arm' is a # genuine 32-bit token and is preserved.) + # - os mingw64/mingw32/ucrt64/clang64/clangarm64 -> msys (G-122; see the + # MSYSTEM-variance note below) + # + #MSYSTEM VARIANCE (cygwin-family hosts on windows). An msys2-runtime tclsh + #reports tcl_platform(platform) 'unix' with an os string msys2's uname + #renders from the MSYSTEM environment variable, so ONE binary canonizes + #differently per launching shell - verified 2026-07-26 with msys2's + #/usr/bin/tclsh8.6: MSYSTEM=MSYS -> MSYS_NT-10.0-26200 (msys-x86_64), and + #MSYSTEM=MINGW64, =UCRT64 or unset (msys2 defaults it) -> MINGW64_NT-10.0-26200 + #(mingw64-x86_64). The MSYSTEM token names the shell that launched the + #process, not the binary's ABI: a genuinely MINGW-built tclsh is a native + #windows binary reporting os 'Windows NT' and canonizes as win32-*. So every + #msys2-runtime token folds to the one stable tag 'msys'. Real cygwin keeps + #its own tag 'cygwin' - a separate runtime dll and mount world, so a store + #tier serving one does not serve the other. Windows build numbers never + #reach a tag (platform::generic cuts the os at its first _ or -; normalize_os + #re-folds a raw uname-style string anyway). + #These are HOST canon names. What a tag implies for BUILD OUTPUT - .exe + #suffixing, which process tooling manages a running artifact, which + #bin/runtime store tier holds a runtime - is a TARGET question that + #src/make.tcl derives from the tag (G-122 host/target split): cygwin-family + #hosts default their build target to win32-x86_64, since the kits they bake + #are native windows binaries. #The special name 'macosx' (no cpu suffix) is the RUNTIME-tier convention for #universal (multi-arch) macOS binaries - punkbin and bin/runtime keep one #universal folder; the per-arch macosx-x86_64/macosx-arm64 names serve the @@ -83,7 +106,8 @@ namespace eval punk::platform { macosx-arm64 {status supported tiers {lib} buildsuite none notes "Apple silicon; would follow a macosx runtime arc"} freebsd-x86_64 {status supported tiers {runtime lib} buildsuite candidate notes ""} freebsd-arm64 {status supported tiers {runtime lib} buildsuite candidate notes "no artifacts yet"} - msys-x86_64 {status dormant tiers {lib} buildsuite none notes "msys/cygwin-built tclsh runtimes; utility under review"} + msys-x86_64 {status dormant tiers {runtime lib} buildsuite none notes "msys2-runtime tclsh on windows (posix personality, .exe files); host canon for every MSYSTEM shell - see normalize"} + cygwin-x86_64 {status recognized tiers {runtime lib} buildsuite none notes "cygwin-runtime tclsh on windows; separate dll/mount world from msys2, so a distinct tier"} openbsd-x86_64 {status recognized tiers {runtime} buildsuite none notes "hosted third-party runtimes possible"} netbsd-x86_64 {status recognized tiers {runtime} buildsuite none notes "hosted third-party runtimes possible"} dragonflybsd-x86_64 {status recognized tiers {runtime} buildsuite none notes "hosted third-party runtimes possible"} @@ -124,6 +148,45 @@ namespace eval punk::platform { return $platforms } + namespace eval argdoc { + variable PUNKARGS + lappend PUNKARGS [list { + @id -id ::punk::platform::normalize_os + @cmd -name punk::platform::normalize_os\ + -summary\ + "Fold an os token to its canonical punkshell spelling."\ + -help\ + "Fold a single os token (the half of an - platform + identifier) to its canonical punkshell spelling: + macos -> macosx + mingw32|mingw64|ucrt64|clang* -> msys + MSYS_NT- -> msys + CYGWIN_NT- -> cygwin + The msys2 family folds to one tag because msys2's uname renders + the os string from the MSYSTEM environment variable: the same + binary reports MSYS_NT or MINGW64_NT depending on which shell + launched it, while a genuinely MINGW-built (native windows) + tclsh reports 'Windows NT' and canonizes as win32. Raw + uname-style strings carrying a windows build number fold too - + a build number never belongs in a platform tag. + Unrecognized tokens pass through unchanged." + @values -min 1 -max 1 + os -type string -help\ + "os token, e.g the half of platform::generic's result." + }] + } + proc normalize_os {os} { + switch -glob -- [string tolower $os] { + macos {return macosx} + msys - msys_nt-* {return msys} + mingw32 - mingw64 - mingw_nt-* - mingw32_nt-* - mingw64_nt-* {return msys} + ucrt64 - ucrt64_nt-* {return msys} + clang32 - clang64 - clangarm64 - clang32_nt-* - clang64_nt-* - clangarm64_nt-* {return msys} + cygwin_nt-* {return cygwin} + } + return $os + } + namespace eval argdoc { variable PUNKARGS lappend PUNKARGS [list { @@ -137,6 +200,9 @@ namespace eval punk::platform { platform-dir name: cpu amd64 -> x86_64, aarch64 -> arm64 os macos -> macosx (Tcl platform 1.1.x renamed modern macOS) + os mingw64/mingw32/ucrt64/clang* -> msys (one stable tag for + the msys2 runtime family - the reported name follows MSYSTEM, + not the binary; see punk::platform::normalize_os) macosx cpu arm -> arm64 (platform::generic's arm* glob folds the darwin arm64 machine name to arm; Apple silicon is 64-bit only - non-macosx 'arm' stays 32-bit arm) @@ -150,18 +216,13 @@ namespace eval punk::platform { proc normalize {platform} { set parts [split $platform -] if {[llength $parts] < 2} { - #single-token names (e.g the universal 'macosx') - fold macos only - if {$platform eq "macos"} { - return macosx - } - return $platform + #single-token names (e.g the universal 'macosx') - fold the os token only + return [normalize_os $platform] } #os may itself contain a dash in principle - treat last token as cpu set cpu [lindex $parts end] set os [join [lrange $parts 0 end-1] -] - switch -- $os { - macos {set os macosx} - } + set os [normalize_os $os] switch -- $cpu { amd64 {set cpu x86_64} aarch64 {set cpu arm64} @@ -232,6 +293,6 @@ namespace eval ::punk::args::register { ## Ready package provide punk::platform [namespace eval punk::platform { variable version - set version 0.1.0 + set version 0.2.0 }] return diff --git a/src/bootsupport/modules/punk/zip-0.1.1.tm b/src/bootsupport/modules/punk/zip-0.1.1.tm deleted file mode 100644 index 02415ccd..00000000 --- a/src/bootsupport/modules/punk/zip-0.1.1.tm +++ /dev/null @@ -1,914 +0,0 @@ -# -*- tcl -*- -# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from -buildversion.txt -# module template: punkshell/src/decktemplates/vendor/punk/modules/template_module-0.0.3.tm -# -# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. -# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -# (C) 2024 JMN -# (C) 2009 Path Thoyts -# -# @@ Meta Begin -# Application punk::zip 0.1.1 -# Meta platform tcl -# Meta license MIT -# @@ Meta End - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -# doctools header -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -#*** !doctools -#[manpage_begin punkshell_module_punk::zip 0 0.1.1] -#[copyright "2024"] -#[titledesc {Module API}] [comment {-- Name section and table of contents description --}] -#[moddesc {-}] [comment {-- Description at end of page heading --}] -#[require punk::zip] -#[keywords module zip fileformat] -#[description] -#[para] - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - -#*** !doctools -#[section Overview] -#[para] overview of punk::zip -#[subsection Concepts] -#[para] - - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -## Requirements -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - -#*** !doctools -#[subsection dependencies] -#[para] packages used by punk::zip -#[list_begin itemized] - -package require Tcl 8.6- -package require punk::args -#*** !doctools -#[item] [package {Tcl 8.6}] -#[item] [package {punk::args}] - -#*** !doctools -#[list_end] - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - -#*** !doctools -#[section API] - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -# Base namespace -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -tcl::namespace::eval punk::zip { - tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase - #variable xyz - - #*** !doctools - #[subsection {Namespace punk::zip}] - #[para] Core API functions for punk::zip - #[list_begin definitions] - - proc Path_a_atorbelow_b {path_a path_b} { - return [expr {[StripPath $path_b $path_a] ne $path_a}] - } - proc Path_a_at_b {path_a path_b} { - return [expr {[StripPath $path_a $path_b] eq "." }] - } - - proc Path_strip_alreadynormalized_prefixdepth {path prefix} { - if {$prefix eq ""} { - return $path - } - set pathparts [file split $path] - set prefixparts [file split $prefix] - if {[llength $prefixparts] >= [llength $pathparts]} { - return "" - } - return [file join \ - {*}[lrange \ - $pathparts \ - [llength $prefixparts] \ - end]] - } - - #StripPath - borrowed from tcllib fileutil - # ::fileutil::stripPath -- - # - # If the specified path references/is a path in prefix (or prefix itself) it - # is made relative to prefix. Otherwise it is left unchanged. - # In the case of it being prefix itself the result is the string '.'. - # - # Arguments: - # prefix prefix to strip from the path. - # path path to modify - # - # Results: - # path The (possibly) modified path. - - if {[string equal $::tcl_platform(platform) windows]} { - # Windows. While paths are stored with letter-case preserved al - # comparisons have to be done case-insensitive. For reference see - # SF Tcllib Bug 2499641. - - proc StripPath {prefix path} { - # [file split] is used to generate a canonical form for both - # paths, for easy comparison, and also one which is easy to modify - # using list commands. - - set prefix [file split $prefix] - set npath [file split $path] - - if {[string equal -nocase $prefix $npath]} { - return "." - } - - if {[string match -nocase "${prefix} *" $npath]} { - set path [eval [linsert [lrange $npath [llength $prefix] end] 0 file join ]] - } - return $path - } - } else { - proc StripPath {prefix path} { - # [file split] is used to generate a canonical form for both - # paths, for easy comparison, and also one which is easy to modify - # using list commands. - - set prefix [file split $prefix] - set npath [file split $path] - - if {[string equal $prefix $npath]} { - return "." - } - - if {[string match "${prefix} *" $npath]} { - set path [eval [linsert [lrange $npath [llength $prefix] end] 0 file join ]] - } - return $path - } - } - - proc Timet_to_dos {time_t} { - #*** !doctools - #[call [fun Timet_to_dos] [arg time_t]] - #[para] convert a unix timestamp into a DOS timestamp for ZIP times. - #[example { - # DOS timestamps are 32 bits split into bit regions as follows: - # 24 16 8 0 - # +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - # |Y|Y|Y|Y|Y|Y|Y|m| |m|m|m|d|d|d|d|d| |h|h|h|h|h|m|m|m| |m|m|m|s|s|s|s|s| - # +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - #}] - set s [clock format $time_t -format {%Y %m %e %k %M %S}] - scan $s {%d %d %d %d %d %d} year month day hour min sec - expr {(($year-1980) << 25) | ($month << 21) | ($day << 16) - | ($hour << 11) | ($min << 5) | ($sec >> 1)} - } - punk::args::define { - @id -id ::punk::zip::walk - @cmd -name punk::zip::walk -help\ - "Walk the directory structure starting at base/<-subpath> - and return a list of the files and folders encountered. - Resulting paths are relative to base unless -resultrelative - is supplied. - Folder names will end with a trailing slash. - " - -resultrelative -optional 1 -help\ - "Resulting paths are relative to this value. - Defaults to the value of base. If empty string - is given to -resultrelative the paths returned - are effectively absolute paths." - -emptydirs -default 0 -type boolean -help\ - "Whether to include directory trees in the result which had no - matches for the given fileglobs. - Intermediate dirs are always returned if there is a match with - fileglobs further down even if -emptdirs is 0. - " - -excludes -default "" -help "list of glob expressions to match against files and exclude" - -subpath -default "" -help\ - "May contain glob chars for folder elements" - #If we don't include --, the call walk -- .. will return nothing as 'base' will receive the -- - -- -type none -optional 1 - @values -min 1 -max -1 - base - fileglobs -default {*} -multiple 1 - } - proc walk {args} { - #*** !doctools - #[call [fun walk] [arg ?options?] [arg base]] - #[para] Walk a directory tree rooted at base - #[para] the -excludes list can be a set of glob expressions to match against files and avoid - #[para] e.g - #[example { - # punk::zip::walk -exclude {CVS/* *~.#*} library - #}] - - #todo: -relative 0|1 flag? - set argd [punk::args::parse $args withid ::punk::zip::walk] - set base [dict get $argd values base] - set fileglobs [dict get $argd values fileglobs] - set subpath [dict get $argd opts -subpath] - set excludes [dict get $argd opts -excludes] - set emptydirs [dict get $argd opts -emptydirs] - - set received [dict get $argd received] - - set imatch [list] - foreach fg $fileglobs { - lappend imatch [file join $subpath $fg] - } - - if {![dict exists $received -resultrelative]} { - set relto $base - set prefix "" - } else { - set relto [file normalize [dict get $argd opts -resultrelative]] - if {$relto ne ""} { - if {![Path_a_atorbelow_b $base $relto]} { - error "punk::zip::walk base must be at or below -resultrelative value (backtracking not currently supported)" - } - set prefix [Path_strip_alreadynormalized_prefixdepth $base $relto] - } else { - set prefix $base - } - } - - set result {} - #set imatch [file join $subpath $match] - set files [glob -nocomplain -tails -types f -directory $base -- {*}$imatch] - foreach file $files { - set excluded 0 - foreach glob $excludes { - if {[string match $glob $file]} { - set excluded 1 - break - } - } - if {!$excluded} {lappend result [file join $prefix $file]} - } - foreach dir [glob -nocomplain -tails -types d -directory $base -- [file join $subpath *]] { - set submatches [walk -subpath $dir -emptydirs $emptydirs -excludes $excludes $base {*}$fileglobs] - set subdir_entries [list] - set thisdir_match [list] - set has_file 0 - foreach sd $submatches { - set fullpath [file join $prefix $sd] ;#file join destroys trailing slash - if {[string index $sd end] eq "/"} { - lappend subdir_entries $fullpath/ - } else { - set has_file 1 - lappend subdir_entries $fullpath - } - } - if {$emptydirs} { - set thisdir_match [list "[file join $prefix $dir]/"] - } else { - if {$has_file} { - set thisdir_match [list "[file join $prefix $dir]/"] - } else { - set subdir_entries [list] - } - } - #NOTE: trailing slash required for entries to be recognised as 'file type' = "directory" - #This is true for 2024 Tcl9 mounted zipfs at least. zip utilities such as 7zip seem(icon correct) to recognize dirs with or without trailing slash - #Although there are attributes on some systems to specify if entry is a directory - it appears trailing slash should always be used for folder names. - set result [list {*}$result {*}$thisdir_match {*}$subdir_entries] - } - return $result - } - - #if there is an external preamble - extract that. (if there is also an internal preamble - ignore and consider part of the archive-data) - #Otherwise extract an internal preamble. - #if neither -? - #review - reconsider auto-determination of internal vs external preamble - punk::args::define { - @id -id ::punk::zip::extract_preamble - @cmd -name punk::zip::extract_preamble -help\ - "Split a zipfs based executable or library into its constituent - binary and zip parts. - - Note that the binary preamble might be either 'within' the zip offsets, - or simply catenated prior to an unadjusted zip. - Some build processes may have 'adjusted' the zip offsets to make the zip cover the entire file - ('file based' offset) whilst the more modern approach is to simply concatenate the binary and the zip - ('archive based' offset). An archive-based offset is simpler and more reliably points to the proper - split location. It also allows 'zipfs info //zipfs:/app' to return the correct offset information. - - Either way, extract_preamble can usually separate them, but in the unusual case that there is both an - external preamble and a preamble within the zip, only the external preamble will be split, with the - internal one remaining in the zip. - - The inverse of this process would be to extract the .zip file created by this split to a folder, - e.g extracted_zip_folder (adjusting contents as required) and then to run: - zipfs mkimg newbinaryname.exe extracted_zip_folder \"\" - " - @values -min 2 -max 3 - infile -type file -optional 0 -help\ - "Name of existing tcl executable or shared lib with attached zipfs filesystem" - outfile_preamble -optional 0 -type file -help\ - "Name of output file for binary preamble to be extracted to. - If this file already exists, an error will be raised" - outfile_zip -default "" -type file -help\ - "Name of output file for zip data to be extracted to. - If this file already exists, an error will be raised" - } - proc extract_preamble {args} { - set argd [punk::args::parse $args withid ::punk::zip::extract_preamble] - lassign [dict values $argd] leaders opts values received - - set infile [dict get $values infile] - set outfile_preamble [dict get $values outfile_preamble] - set outfile_zip [dict get $values outfile_zip] - - set inzip [open $infile r] - fconfigure $inzip -encoding iso8859-1 -translation binary - if {[file exists $outfile_preamble]} { - error "outfile_preamble $outfile_preamble already exists - please remove first" - } - if {$outfile_zip ne ""} { - if {[file exists $outfile_zip] && [file size $outfile_zip]} { - error "outfile_zip $outfile_zip already exists - please remove first" - } - } - chan seek $inzip 0 end - set insize [tell $inzip] ;#faster (including seeks) than calling out to filesystem using file size - but should be equivalent - chan seek $inzip 0 start - #only scan last 64k - cover max signature size?? review - if {$insize < 65559} { - set tailsearch_start 0 - } else { - set tailsearch_start [expr {$insize - 65559}] - } - chan seek $inzip $tailsearch_start start - set scan [read $inzip] - #EOCD - End Of Central Directory record - set start_of_end [string last "\x50\x4b\x05\x06" $scan] - puts stdout "==>start_of_end: $start_of_end" - - if {$start_of_end == -1} { - #no zip eocdr - consider entire file to be the zip preamble - set baseoffset $insize - } else { - set filerelative_eocd_posn [expr {$start_of_end + $tailsearch_start}] - chan seek $inzip $filerelative_eocd_posn - set cdir_record_plus [read $inzip] ;#can have trailing data - binary scan $cdir_record_plus issssiis eocd(signature) eocd(disknbr) eocd(ctrldirdisk) \ - eocd(numondisk) eocd(totalnum) eocd(dirsize) eocd(diroffset) eocd(comment_len) - #rule out a false positive from within a nonzip (e.g plain exe) - #There exists for example a PK\5\6 in a plain tclsh, but it doesn't appear to be zip related. - #It doesn't seem to occur near the end - so perhaps not an issue - but we'll do some basic checks anyway - #we only support single disk - so we'll validate a bit more by requiring disknbr and ctrldirdisk to be zeros - #todo - just search for Pk\5\6\0\0\0\0 in the first place? //review - if {$eocd(disknbr) + $eocd(ctrldirdisk) != 0} { - #review - should keep searching? - #for now we assume not a zip - set baseoffset $insize - } else { - #use the central dir size to jump back tko start of central dir - #determine if diroffset is file or archive relative - - set filerelative_cdir_start [expr {$filerelative_eocd_posn - $eocd(dirsize)}] - puts stdout "---> [read $inzip 4]" - if {$filerelative_cdir_start > $eocd(diroffset)} { - #'external preamble' easy case - # - ie 'archive' offset - (and one of the reasons I prefer archive-offset - it makes finding the 'prefix' easier - #though we are assuming zip offsets are not corrupted - set baseoffset [expr {$filerelative_cdir_start - $eocd(diroffset)}] - } else { - #'internal preamble' hard case - # - either no preamble - or offsets have been adjusted to be file relative. - #we could scan from top (ugly) - and with binary prefixes we could get false positives in the data that look like PK\3\4 headers - #we could either work out the format for all possible executables that could be appended (across all platforms) and understand where they end? - #or we just look for the topmost PK\3\4 header pointed to by a CDR record - and assume the CDR is complete - - #step one - read all the CD records and find the highest pointed to local file record (which isn't necessarily the first - but should get us above most if not all of the zip data) - #we can't assume they're ordered in any particular way - so we in theory have to look at them all. - set baseoffset "unknown" - chan seek $inzip $filerelative_cdir_start start - #binary scan $cdir_record_plus issssiis eocd(signature) eocd(disknbr) eocd(ctrldirdisk) \ - # eocd(numondisk) eocd(totalnum) eocd(dirsize) eocd(diroffset) eocd(comment_len) - #load the whole central dir into cdir - - #todo! loop through all cdr file headers - find highest offset? - #tclZipfs.c just looks at first file header in Central Directory - #looking at all entries would be more robust - but we won't work harder than tclZipfs.c for now //REVIEW - - set cdirdata [read $inzip $eocd(dirsize)] - binary scan $cdirdata issssssiiisssssii cdir(signature) cdir(_vermadeby) cdir(_verneeded) cdir(gpbitflag) cdir(compmethod) cdir(lastmodifiedtime) cdir(lastmodifieddate)\ - cdir(uncompressedcrc32) cdir(compressedsize) cdir(uncompressedsize) cdir(filenamelength) cdir(extrafieldlength) cdir(filecommentlength) cdir(disknbr)\ - cdir(internalfileattributes) cdir(externalfileatributes) cdir(relativeoffset) - - #since we're in this branch - we assume cdir(relativeoffset) is from the start of the file - chan seek $inzip $cdir(relativeoffset) - #let's at least check that we landed on a local file header.. - set local_file_header_beginning [read $inzip 28]; #local_file_header without the file name and extra field - binary scan $local_file_header_beginning isssssiiiss lfh(signature) lfh(_verneeded) lfh(gpbitflag) lfh(compmethod) lfh(lastmodifiedtime) lfh(lastmodifieddate)\ - lfh(uncompressedcrc32) lfh(compressedsize) lfh(uncompressedsize) lfh(filenamelength) lfh(extrafieldlength) - #dec2hex 67324752 = 4034B50 = PK\3\4 - puts stdout "1st local file header sig: $lfh(signature)" - if {$lfh(signature) == 67324752} { - #looks like a local file header - #use our cdir(relativeoffset) as the start of the zip-data (//review - possible embedded password + end marker preceeding this) - set baseoffset $cdir(relativeoffset) - } - } - puts stdout "filerel_cdirstart: $filerelative_cdir_start recorded_offset: $eocd(diroffset)" - } - } - puts stdout "baseoffset: $baseoffset" - #expect CDFH PK\1\2 - #above the CD - we expect a bunch of PK\3\4 records - (possibly not all of them pointed to by the CDR) - #above that we expect: *possibly* a stored password with trailing marker - then the prefixed exe/script - - if {![string is integer -strict $baseoffset]} { - error "unable to determine zip baseoffset of file $infile" - } - - if {$baseoffset < $insize} { - set pout [open $outfile_preamble w] - fconfigure $pout -encoding iso8859-1 -translation binary - chan seek $inzip 0 start - chan copy $inzip $pout -size $baseoffset - close $pout - if {$outfile_zip ne ""} { - #todo - if it was internal preamble - need to adjust offsets to fix the split off zipfile - set zout [open $outfile_zip w] - fconfigure $zout -encoding iso8859-1 -translation binary - chan copy $inzip $zout - close $zout - } - close $inzip - } else { - #no valid (from our perspective) eocdr found - baseoffset has been set to insize - close $inzip - file copy $infile $outfile_preamble - if {$outfile_zip ne ""} { - #touch equiv? - set fd [open $outfile_zip w] - close $fd - } - } - } - - - - punk::args::define { - @id -id ::punk::zip::Addentry - @cmd -name punk::zip::Addentry\ - -summary\ - "Add zip-entry for file at 'path'"\ - -help\ - "Add a single file at 'path' to open channel 'zipchan' - return a central directory file record" - @opts - -comment -default "" -help "An optional comment specific to the added file" - @values -min 3 -max 4 - zipchan -help "open file descriptor with cursor at position appropriate for writing a local file header" - base -help "base path for entries" - path -type file -help "path of file to add" - zipdataoffset -default 0 -type integer -range {0 ""} -help "offset of start of zip-data - ie length of prefixing script/exe - Can be specified as zero even if a prefix exists - which would make offsets 'file relative' as opposed to 'archive relative'" - } - - # Addentry - was Mkzipfile -- - # - # FIX ME: should handle the current offset for non-seekable channels - # - proc Addentry {args} { - #*** !doctools - #[call [fun Addentry] [arg zipchan] [arg base] [arg path] [arg ?comment?]] - #[para] Add a single file to a zip archive - #[para] The zipchan channel should already be open and binary. - #[para] You can provide a -comment for the file. - #[para] The return value is the central directory record that will need to be used when finalizing the zip archive. - - set argd [punk::args::parse $args withid ::punk::zip::Addentry] - set zipchan [dict get $argd values zipchan] - set base [dict get $argd values base] - set path [dict get $argd values path] - set zipdataoffset [dict get $argd values zipdataoffset] - - set comment [dict get $argd opts -comment] - - set fullpath [file join $base $path] - set mtime [Timet_to_dos [file mtime $fullpath]] - set utfpath [encoding convertto utf-8 $path] - set utfcomment [encoding convertto utf-8 $comment] - set flags [expr {(1<<11)}] ;# utf-8 comment and path - set method 0 ;# store 0, deflate 8 - set attr 0 ;# text or binary (default binary) - set version 20 ;# minumum version req'd to extract - set extra "" - set crc 0 - set size 0 - set csize 0 - set data "" - set seekable [expr {[tell $zipchan] != -1}] - if {[file isdirectory $fullpath]} { - set attrex 0x41ff0010 ;# 0o040777 (drwxrwxrwx) - #set attrex 0x40000010 - } elseif {[file executable $fullpath]} { - set attrex 0x81ff0080 ;# 0o100777 (-rwxrwxrwx) - } else { - set attrex 0x81b60020 ;# 0o100666 (-rw-rw-rw-) - if {[file extension $fullpath] in {".tcl" ".txt" ".c"}} { - set attr 1 ;# text - } - } - - if {[file isfile $fullpath]} { - set size [file size $fullpath] - if {!$seekable} {set flags [expr {$flags | (1 << 3)}]} - } - - - set channeloffset [tell $zipchan] ;#position in the channel - this may include prefixing exe/zip - set local [binary format a4sssiiiiss PK\03\04 \ - $version $flags $method $mtime $crc $csize $size \ - [string length $utfpath] [string length $extra]] - append local $utfpath $extra - puts -nonewline $zipchan $local - - if {[file isfile $fullpath]} { - # If the file is under 2MB then zip in one chunk, otherwize we use - # streaming to avoid requiring excess memory. This helps to prevent - # storing re-compressed data that may be larger than the source when - # handling PNG or JPEG or nested ZIP files. - if {$size < 0x00200000} { - set fin [open $fullpath rb] - set data [read $fin] - set crc [zlib crc32 $data] - set cdata [zlib deflate $data] - if {[string length $cdata] < $size} { - set method 8 - set data $cdata - } - close $fin - set csize [string length $data] - puts -nonewline $zipchan $data - } else { - set method 8 - set fin [open $fullpath rb] - set zlib [zlib stream deflate] - while {![eof $fin]} { - set data [read $fin 4096] - set crc [zlib crc32 $data $crc] - $zlib put $data - if {[string length [set zdata [$zlib get]]]} { - incr csize [string length $zdata] - puts -nonewline $zipchan $zdata - } - } - close $fin - $zlib finalize - set zdata [$zlib get] - incr csize [string length $zdata] - puts -nonewline $zipchan $zdata - $zlib close - } - - if {$seekable} { - # update the header if the output is seekable - set local [binary format a4sssiiii PK\03\04 \ - $version $flags $method $mtime $crc $csize $size] - set current [tell $zipchan] - seek $zipchan $channeloffset - puts -nonewline $zipchan $local - seek $zipchan $current - } else { - # Write a data descriptor record - set ddesc [binary format a4iii PK\7\8 $crc $csize $size] - puts -nonewline $zipchan $ddesc - } - } - - #PK\x01\x02 Cdentral directory file header - #set v1 0x0317 ;#upper byte 03 -> UNIX lower byte 23 -> 2.3 - set v1 0x0017 ;#upper byte 00 -> MS_DOS and OS/2 (FAT/VFAT/FAT32 file systems) - - set hdr [binary format a4ssssiiiisssssii PK\01\02 $v1 \ - $version $flags $method $mtime $crc $csize $size \ - [string length $utfpath] [string length $extra]\ - [string length $utfcomment] 0 $attr $attrex [expr {$channeloffset - $zipdataoffset}]] ;#zipdataoffset may be zero - either because it's a pure zip, or file-based offsets desired. - append hdr $utfpath $extra $utfcomment - return $hdr - } - - #### REVIEW!!! - #JMN - review - this looks to be offset relative to start of file - (same as 2024 Tcl 'mkzip mkimg') - # we want to enable (optionally) offsets relative to start of archive for exe/script-prefixed zips.on windows (editability with 7z,peazip) - #### - - - punk::args::define { - @id -id ::punk::zip::mkzip - @cmd -name punk::zip::mkzip\ - -summary\ - "Create a zip archive in 'filename'."\ - -help\ - "Create a zip archive in 'filename'" - @opts - -offsettype -default "archive" -choices {archive file}\ - -help\ - "zip offsets stored relative to start of entire file or relative to start of zip-archive - Only relevant if the created file has a script/runtime prefix." - -return -default "pretty" -choices {pretty list none}\ - -help\ - "mkzip can return a list of the files and folders added to the archive - the option -return pretty is the default and uses the punk::lib pdict/plist system - to return a formatted list for the terminal - " - -zipkit -default 0 -type none\ - -help\ - "whether to add mounting script - mutually exclusive with -runtime option - currently vfs::zip based - todo - autodetect zipfs/vfs with pref for zipfs" - -runtime -default ""\ - -help\ - "specify a prefix file - e.g punk::zip::mkzip -runtime unzipsfx.exe -directory subdir -base subdir output.zip - will create a self-extracting zip archive from the subdir/ folder. - Expects runtime with no existing vfs attached (review)" - -comment -default ""\ - -help "An optional comment for the archive" - -directory -default ""\ - -help "Scan for contents within this folder or current directory if not provided." - -base -default ""\ - -help\ - "The new zip archive will be rooted in this directory if provided - it must be a parent of -directory or the same path as -directory" - -exclude -default {CVS/* */CVS/* *~ ".#*" "*/.#*"} - -- -type none -help\ - "End of options marker" - - @values -min 1 -max -1 - filename -type file -default ""\ - -help "name of zipfile to create" - globs -default {*} -multiple 1\ - -help\ - "list of glob patterns to match. - Only directories with matching files will be included in the archive." - } - - # zip::mkzip -- - # - # eg: zip my.zip -directory Subdir -runtime unzipsfx.exe *.txt - # - proc mkzip {args} { - #todo - doctools - [arg ?globs...?] syntax? - - #*** !doctools - #[call [fun mkzip]\ - # [opt "[option -offsettype] [arg offsettype]"]\ - # [opt "[option -return] [arg returntype]"]\ - # [opt "[option -zipkit] [arg 0|1]"]\ - # [opt "[option -runtime] [arg preamble_filename]"]\ - # [opt "[option -comment] [arg zipfilecomment]"]\ - # [opt "[option -directory] [arg dir_to_zip]"]\ - # [opt "[option -base] [arg archive_root]"]\ - # [opt "[option -exclude] [arg globlist]"]\ - # [arg zipfilename]\ - # [arg ?glob...?]] - #[para] Create a zip archive in 'zipfilename' - #[para] If a file already exists, an error will be raised. - #[para] Call 'punk::zip::mkzip' with no arguments for usage display. - - set argd [punk::args::parse $args withid ::punk::zip::mkzip] - set filename [dict get $argd values filename] - if {$filename eq ""} { - error "mkzip filename cannot be empty string" - } - if {[regexp {[?*]} $filename]} { - #catch a likely error where filename is omitted and first glob pattern is misinterpreted as zipfile name - error "mkzip filename should not contain glob characters ? *" - } - if {[file exists $filename]} { - error "mkzip filename:$filename already exists" - } - dict for {k v} [dict get $argd opts] { - switch -- $k { - -comment { - dict set argd opts $k [encoding convertto utf-8 $v] - } - -directory - -base { - dict set argd opts $k [file normalize $v] - } - } - } - - array set opts [dict get $argd opts] - - - if {$opts(-directory) ne ""} { - if {$opts(-base) ne ""} { - #-base and -directory have been normalized already - if {![Path_a_atorbelow_b $opts(-directory) $opts(-base)]} { - error "punk::zip::mkzip -base $opts(-base) must be above or the same as -directory $opts(-directory)" - } - set base $opts(-base) - set relpath [Path_strip_alreadynormalized_prefixdepth $opts(-directory) $opts(-base)] - } else { - set base $opts(-directory) - set relpath "" - } - #will pick up intermediary folders as paths (ending with trailing slash) - set paths [walk -exclude $opts(-exclude) -subpath $relpath -- $base {*}[dict get $argd values globs]] - - set norm_filename [file normalize $filename] - set norm_dir [file normalize $opts(-directory)] ;#we only care if filename below -directory (which is where we start scanning) - if {[Path_a_atorbelow_b $norm_filename $norm_dir]} { - #check that we aren't adding the zipfile to itself - #REVIEW - now that we open zipfile after scanning - this isn't really a concern! - #keep for now in case we can add an -update or a -force facility (or in case we modify to add to zip as we scan for members?) - #In the case of -force - we may want to delay replacement of original until scan is done? - - #try to avoid looping on all paths and performing (somewhat) expensive file normalizations on each - #1st step is to check the patterns and see if our zipfile is already excluded - in which case we need not check the paths - set self_globs_match 0 - foreach g [dict get $argd values globs] { - if {[string match $g [file tail $filename]]} { - set self_globs_match 1 - break - } - } - if {$self_globs_match} { - #still dangerous - set self_excluded 0 - foreach e $opts(-exclude) { - if {[string match $e [file tail $filename]]} { - set self_excluded 1 - break - } - } - if {!$self_excluded} { - #still dangerous - likely to be in resultset - check each path - #puts stderr "zip file $filename is below directory $opts(-directory)" - set self_is_matched 0 - set i 0 - foreach p $paths { - set norm_p [file normalize [file join $opts(-directory) $p]] - if {[Path_a_at_b $norm_filename $norm_p]} { - set self_is_matched 1 - break - } - incr i - } - if {$self_is_matched} { - puts stderr "WARNING - zipfile being created '$filename' was matched. Excluding this file. Relocate the zip, or use -exclude patterns to avoid this message" - set paths [lremove $paths $i] - } - } - } - } - } else { - #NOTE that we don't add intermediate folders when creating an archive without using the -directory flag! - #ie - only the exact *files* matching the glob are stored. - set paths [list] - set dir [pwd] - if {$opts(-base) ne ""} { - if {![Path_a_atorbelow_b $dir $opts(-base)]} { - error "punk::zip::mkzip -base $opts(-base) must be above current directory" - } - set relpath [Path_strip_alreadynormalized_prefixdepth [file normalize $dir] [file normalize $opts(-base)]] - } else { - set relpath "" - } - set base $opts(-base) - - set matches [glob -nocomplain -type f -- {*}[dict get $argd values globs]] - foreach m $matches { - if {$m eq $filename} { - #puts stderr "--> excluding $filename" - continue - } - set isok 1 - foreach e [concat $opts(-exclude) $filename] { - if {[string match $e $m]} { - set isok 0 - break - } - } - if {$isok} { - lappend paths [file join $relpath $m] - } - } - } - - if {![llength $paths]} { - return "" - } - - set zf [open $filename wb] - if {$opts(-runtime) ne ""} { - #todo - strip any existing vfs - option to merge contents.. only if zip attached? - set rt [open $opts(-runtime) rb] - fcopy $rt $zf - close $rt - } elseif {$opts(-zipkit)} { - #TODO - update to zipfs ? - #see modpod - set zkd "#!/usr/bin/env tclkit\n\# This is a zip-based Tcl Module\n" - append zkd "package require vfs::zip\n" - append zkd "vfs::zip::Mount \[info script\] \[info script\]\n" - append zkd "if {\[file exists \[file join \[info script\] main.tcl\]\]} {\n" - append zkd " source \[file join \[info script\] main.tcl\]\n" - append zkd "}\n" - append zkd \x1A - puts -nonewline $zf $zkd - } - - #todo - subtract this from the endrec offset - if {$opts(-offsettype) eq "archive"} { - set dataStartOffset [tell $zf] ;#the overall file offset of the start of archive-data //JMN 2024 - } else { - set dataStartOffset 0 ;#offsets relative to file - the (old) zipfs mkzip way :/ - } - - set count 0 - set cd "" - - set members [list] - foreach path $paths { - #puts $path - lappend members $path - append cd [Addentry $zf $base $path $dataStartOffset] ;#path already includes relpath - incr count - } - set cdoffset [tell $zf] - set endrec [binary format a4ssssiis PK\05\06 0 0 \ - $count $count [string length $cd] [expr {$cdoffset - $dataStartOffset}]\ - [string length $opts(-comment)]] - append endrec $opts(-comment) - puts -nonewline $zf $cd - puts -nonewline $zf $endrec - close $zf - - set result "" - switch -exact -- $opts(-return) { - list { - set result $members - } - pretty { - if {[info commands showlist] ne ""} { - set result [plist -channel none members] - } else { - set result $members - } - } - none { - set result "" - } - } - return $result - } - - - #*** !doctools - #[list_end] [comment {--- end definitions namespace punk::zip ---}] -} -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -# Secondary API namespace -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -tcl::namespace::eval punk::zip::lib { - tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase - tcl::namespace::path [tcl::namespace::parent] - #*** !doctools - #[subsection {Namespace punk::zip::lib}] - #[para] Secondary functions that are part of the API - #[list_begin definitions] - - #proc utility1 {p1 args} { - # #*** !doctools - # #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]] - # #[para]Description of utility1 - # return 1 - #} - - - - #*** !doctools - #[list_end] [comment {--- end definitions namespace punk::zip::lib ---}] -} -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -## Ready -package provide punk::zip [tcl::namespace::eval punk::zip { - variable pkg punk::zip - variable version - set version 0.1.1 -}] -return - -#*** !doctools -#[manpage_end] - diff --git a/src/bootsupport/modules/punk/zip-0.2.0.tm b/src/bootsupport/modules/punk/zip-0.2.0.tm new file mode 100644 index 00000000..eb1651da --- /dev/null +++ b/src/bootsupport/modules/punk/zip-0.2.0.tm @@ -0,0 +1,1698 @@ +# -*- tcl -*- +# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from -buildversion.txt +# module template: punkshell/src/decktemplates/vendor/punk/modules/template_module-0.0.3.tm +# +# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. +# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# (C) 2024 JMN +# (C) 2009 Path Thoyts +# +# @@ Meta Begin +# Application punk::zip 0.2.0 +# Meta platform tcl +# Meta license MIT +# @@ Meta End + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# doctools header +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +#*** !doctools +#[manpage_begin punkshell_module_punk::zip 0 0.2.0] +#[copyright "2024"] +#[titledesc {Module API}] [comment {-- Name section and table of contents description --}] +#[moddesc {-}] [comment {-- Description at end of page heading --}] +#[require punk::zip] +#[keywords module zip fileformat] +#[description] +#[para] - + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + +#*** !doctools +#[section Overview] +#[para] overview of punk::zip +#[subsection Concepts] +#[para] - + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +## Requirements +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + +#*** !doctools +#[subsection dependencies] +#[para] packages used by punk::zip +#[list_begin itemized] + +package require Tcl 8.6- +package require punk::args +#*** !doctools +#[item] [package {Tcl 8.6}] +#[item] [package {punk::args}] + +#*** !doctools +#[list_end] + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + +#*** !doctools +#[section API] + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# Base namespace +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +tcl::namespace::eval punk::zip { + tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase + #variable xyz + + #*** !doctools + #[subsection {Namespace punk::zip}] + #[para] Core API functions for punk::zip + #[list_begin definitions] + + proc Path_a_atorbelow_b {path_a path_b} { + return [expr {[StripPath $path_b $path_a] ne $path_a}] + } + proc Path_a_at_b {path_a path_b} { + return [expr {[StripPath $path_a $path_b] eq "." }] + } + + proc Path_strip_alreadynormalized_prefixdepth {path prefix} { + if {$prefix eq ""} { + return $path + } + set pathparts [file split $path] + set prefixparts [file split $prefix] + if {[llength $prefixparts] >= [llength $pathparts]} { + return "" + } + return [file join \ + {*}[lrange \ + $pathparts \ + [llength $prefixparts] \ + end]] + } + + #StripPath - borrowed from tcllib fileutil + # ::fileutil::stripPath -- + # + # If the specified path references/is a path in prefix (or prefix itself) it + # is made relative to prefix. Otherwise it is left unchanged. + # In the case of it being prefix itself the result is the string '.'. + # + # Arguments: + # prefix prefix to strip from the path. + # path path to modify + # + # Results: + # path The (possibly) modified path. + + if {[string equal $::tcl_platform(platform) windows]} { + # Windows. While paths are stored with letter-case preserved al + # comparisons have to be done case-insensitive. For reference see + # SF Tcllib Bug 2499641. + + proc StripPath {prefix path} { + # [file split] is used to generate a canonical form for both + # paths, for easy comparison, and also one which is easy to modify + # using list commands. + + set prefix [file split $prefix] + set npath [file split $path] + + if {[string equal -nocase $prefix $npath]} { + return "." + } + + if {[string match -nocase "${prefix} *" $npath]} { + set path [eval [linsert [lrange $npath [llength $prefix] end] 0 file join ]] + } + return $path + } + } else { + proc StripPath {prefix path} { + # [file split] is used to generate a canonical form for both + # paths, for easy comparison, and also one which is easy to modify + # using list commands. + + set prefix [file split $prefix] + set npath [file split $path] + + if {[string equal $prefix $npath]} { + return "." + } + + if {[string match "${prefix} *" $npath]} { + set path [eval [linsert [lrange $npath [llength $prefix] end] 0 file join ]] + } + return $path + } + } + + proc Timet_to_dos {time_t} { + #*** !doctools + #[call [fun Timet_to_dos] [arg time_t]] + #[para] convert a unix timestamp into a DOS timestamp for ZIP times. + #[example { + # DOS timestamps are 32 bits split into bit regions as follows: + # 24 16 8 0 + # +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + # |Y|Y|Y|Y|Y|Y|Y|m| |m|m|m|d|d|d|d|d| |h|h|h|h|h|m|m|m| |m|m|m|s|s|s|s|s| + # +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + #}] + set s [clock format $time_t -format {%Y %m %e %k %M %S}] + scan $s {%d %d %d %d %d %d} year month day hour min sec + expr {(($year-1980) << 25) | ($month << 21) | ($day << 16) + | ($hour << 11) | ($min << 5) | ($sec >> 1)} + } + + #Inverse of Timet_to_dos. DOS timestamps carry no timezone, and Timet_to_dos + #formats in local time - so this scans in local time to keep the pair symmetric. + #Entries stored with an unrepresentable date (zero, or corrupt) yield 0. + proc Dos_to_timet {dosdatetime} { + set dosdate [expr {($dosdatetime >> 16) & 0xFFFF}] + set dostime [expr {$dosdatetime & 0xFFFF}] + set year [expr {(($dosdate >> 9) & 0x7F) + 1980}] + set month [expr {($dosdate >> 5) & 0x0F}] + set day [expr {$dosdate & 0x1F}] + set hour [expr {($dostime >> 11) & 0x1F}] + set minute [expr {($dostime >> 5) & 0x3F}] + set second [expr {($dostime & 0x1F) << 1}] + if {$month < 1 || $month > 12 || $day < 1 || $day > 31 || $hour > 23 || $minute > 59 || $second > 59} { + return 0 + } + set stamp [format {%04d %02d %02d %02d %02d %02d} $year $month $day $hour $minute $second] + if {[catch {clock scan $stamp -format {%Y %m %d %H %M %S}} timet]} { + return 0 + } + return $timet + } + + #Compression method ids as they appear in a zip central directory. + #punk::zip READS 0 (store) and 8 (deflate); the rest are named so an + #unsupported archive is refused by name rather than by number. + variable methodnames + set methodnames [dict create {*}{ + 0 store + 1 shrink + 2 reduce1 + 3 reduce2 + 4 reduce3 + 5 reduce4 + 6 implode + 8 deflate + 9 deflate64 + 10 pkware-implode + 12 bzip2 + 14 lzma + 16 cmpsc + 18 terse + 19 lz77 + 20 zstd-deprecated + 93 zstd + 94 mp3 + 95 xz + 96 jpeg + 97 wavpack + 98 ppmd + 99 aes + }] + + proc Method_name {method} { + variable methodnames + if {[dict exists $methodnames $method]} { + return [dict get $methodnames $method] + } + return method-$method + } + punk::args::define { + @id -id ::punk::zip::walk + @cmd -name punk::zip::walk -help\ + "Walk the directory structure starting at base/<-subpath> + and return a list of the files and folders encountered. + Resulting paths are relative to base unless -resultrelative + is supplied. + Folder names will end with a trailing slash. + " + -resultrelative -optional 1 -help\ + "Resulting paths are relative to this value. + Defaults to the value of base. If empty string + is given to -resultrelative the paths returned + are effectively absolute paths." + -emptydirs -default 0 -type boolean -help\ + "Whether to include directory trees in the result which had no + matches for the given fileglobs. + Intermediate dirs are always returned if there is a match with + fileglobs further down even if -emptdirs is 0. + " + -excludes -default "" -help "list of glob expressions to match against files and exclude" + -subpath -default "" -help\ + "May contain glob chars for folder elements" + #If we don't include --, the call walk -- .. will return nothing as 'base' will receive the -- + -- -type none -optional 1 + @values -min 1 -max -1 + base + fileglobs -default {*} -multiple 1 + } + proc walk {args} { + #*** !doctools + #[call [fun walk] [arg ?options?] [arg base]] + #[para] Walk a directory tree rooted at base + #[para] the -excludes list can be a set of glob expressions to match against files and avoid + #[para] e.g + #[example { + # punk::zip::walk -exclude {CVS/* *~.#*} library + #}] + + #todo: -relative 0|1 flag? + set argd [punk::args::parse $args withid ::punk::zip::walk] + set base [dict get $argd values base] + set fileglobs [dict get $argd values fileglobs] + set subpath [dict get $argd opts -subpath] + set excludes [dict get $argd opts -excludes] + set emptydirs [dict get $argd opts -emptydirs] + + set received [dict get $argd received] + + set imatch [list] + foreach fg $fileglobs { + lappend imatch [file join $subpath $fg] + } + + if {![dict exists $received -resultrelative]} { + set relto $base + set prefix "" + } else { + set relto [file normalize [dict get $argd opts -resultrelative]] + if {$relto ne ""} { + if {![Path_a_atorbelow_b $base $relto]} { + error "punk::zip::walk base must be at or below -resultrelative value (backtracking not currently supported)" + } + set prefix [Path_strip_alreadynormalized_prefixdepth $base $relto] + } else { + set prefix $base + } + } + + set result {} + #set imatch [file join $subpath $match] + set files [glob -nocomplain -tails -types f -directory $base -- {*}$imatch] + foreach file $files { + set excluded 0 + foreach glob $excludes { + if {[string match $glob $file]} { + set excluded 1 + break + } + } + if {!$excluded} {lappend result [file join $prefix $file]} + } + foreach dir [glob -nocomplain -tails -types d -directory $base -- [file join $subpath *]] { + set submatches [walk -subpath $dir -emptydirs $emptydirs -excludes $excludes $base {*}$fileglobs] + set subdir_entries [list] + set thisdir_match [list] + set has_file 0 + foreach sd $submatches { + set fullpath [file join $prefix $sd] ;#file join destroys trailing slash + if {[string index $sd end] eq "/"} { + lappend subdir_entries $fullpath/ + } else { + set has_file 1 + lappend subdir_entries $fullpath + } + } + if {$emptydirs} { + set thisdir_match [list "[file join $prefix $dir]/"] + } else { + if {$has_file} { + set thisdir_match [list "[file join $prefix $dir]/"] + } else { + set subdir_entries [list] + } + } + #NOTE: trailing slash required for entries to be recognised as 'file type' = "directory" + #This is true for 2024 Tcl9 mounted zipfs at least. zip utilities such as 7zip seem(icon correct) to recognize dirs with or without trailing slash + #Although there are attributes on some systems to specify if entry is a directory - it appears trailing slash should always be used for folder names. + set result [list {*}$result {*}$thisdir_match {*}$subdir_entries] + } + return $result + } + + # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + # Archive structure - the shared reader machinery. + # + # Every reading surface (extract_preamble, archive_info, members, unzip) goes + # through Archive_read, so a plain zip and a zip attached to an executable are + # one code path, and the archive-relative vs file-relative offset convention is + # decided in exactly one place. + # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + + #Locate and validate the End Of Central Directory record. + #Scans candidate PK\5\6 signatures from the end of the file backwards - a plain + #executable can contain that byte sequence in its data, so a candidate is only + #accepted if it describes a single-disk directory that really begins with PK\1\2. + #The first pass also requires the record to end exactly at EOF (comment length + #consistent); a second pass drops that so archives with trailing junk still read. + proc Eocd_scan {chan filesize} { + set info [dict create {*}{ + status nozip + reason {no end-of-central-directory record found} + eocdoffset -1 + cdiroffset -1 + cdirsize 0 + count 0 + diroffset 0 + offsetbase 0 + comment {} + }] + dict set info filesize $filesize + if {$filesize < 22} { + return $info + } + set tailstart [expr {$filesize < 65559 ? 0 : $filesize - 65559}] + chan seek $chan $tailstart start + set tail [read $chan] + set candidates [list] + set idx [string length $tail] + while {1} { + set p [string last "\x50\x4b\x05\x06" $tail [expr {$idx - 1}]] + if {$p < 0} { + break + } + lappend candidates [expr {$p + $tailstart}] + set idx $p + } + foreach strict {1 0} { + foreach eocdoffset $candidates { + set try [Eocd_try $chan $filesize $eocdoffset $strict] + if {[dict get $try status] ne "reject"} { + return $try + } + } + } + return $info + } + + proc Eocd_try {chan filesize eocdoffset strict} { + if {$eocdoffset + 22 > $filesize} { + return [dict create status reject] + } + chan seek $chan $eocdoffset start + set rec [read $chan 22] + binary scan $rec issssiis sig disknbr cdirdisk numondisk totalnum cdirsize diroffset commentlen + set disknbr [expr {$disknbr & 0xFFFF}] + set cdirdisk [expr {$cdirdisk & 0xFFFF}] + set numondisk [expr {$numondisk & 0xFFFF}] + set totalnum [expr {$totalnum & 0xFFFF}] + set commentlen [expr {$commentlen & 0xFFFF}] + set cdirsize [expr {$cdirsize & 0xFFFFFFFF}] + set diroffset [expr {$diroffset & 0xFFFFFFFF}] + #single-disk archives only - anything else is a spanned archive or a false positive + if {$disknbr != 0 || $cdirdisk != 0 || $numondisk != $totalnum} { + return [dict create status reject] + } + if {$strict && $eocdoffset + 22 + $commentlen != $filesize} { + return [dict create status reject] + } + set info [dict create {*}{ + status ok + reason {} + } filesize $filesize {*}{ + } eocdoffset $eocdoffset {*}{ + } cdirsize $cdirsize {*}{ + } count $totalnum {*}{ + } diroffset $diroffset {*}{ + }] + #A saturated field means the real value lives in a zip64 record we do not read. + if {$totalnum == 0xFFFF || $cdirsize == 0xFFFFFFFF || $diroffset == 0xFFFFFFFF} { + dict set info status unsupported + dict set info reason "zip64 archive - punk::zip reads single-disk zip32 archives only" + dict set info cdiroffset -1 + dict set info offsetbase 0 + dict set info comment "" + return $info + } + set cdiroffset [expr {$eocdoffset - $cdirsize}] + if {$cdiroffset < 0} { + return [dict create status reject] + } + if {$totalnum > 0} { + chan seek $chan $cdiroffset start + if {[read $chan 4] ne "\x50\x4b\x01\x02"} { + return [dict create status reject] + } + } elseif {$cdirsize != 0} { + return [dict create status reject] + } + #The recorded directory offset is either archive-relative (the directory sits + #that far past a preamble) or file-relative (it IS the file position). The + #difference between where the directory actually is and where the record says + #it is therefore gives the preamble length directly; negative means neither + #reading holds and this candidate is not a real EOCD. + set offsetbase [expr {$cdiroffset - $diroffset}] + if {$offsetbase < 0} { + return [dict create status reject] + } + set comment "" + if {$commentlen > 0} { + chan seek $chan [expr {$eocdoffset + 22}] start + set comment [read $chan $commentlen] + } + dict set info cdiroffset $cdiroffset + dict set info offsetbase $offsetbase + dict set info comment $comment + return $info + } + + punk::args::define { + @id -id ::punk::zip::Cdir_records + @cmd -name punk::zip::Cdir_records\ + -summary\ + "Parse every central directory record of an archive"\ + -help\ + "Walk ALL central directory file headers and return one member dict per + entry, in stored order. Sizes and offsets come from the central + directory, which sidesteps data descriptors entirely. + See punk::zip::members for the member dict keys." + @values -min 2 -max 2 + chan -help "open binary channel positioned anywhere in the archive" + info -type dict -help "archive dict from Eocd_scan with status ok" + } + proc Cdir_records {chan info} { + set cdiroffset [dict get $info cdiroffset] + set cdirsize [dict get $info cdirsize] + set count [dict get $info count] + set offsetbase [dict get $info offsetbase] + if {$count == 0} { + return [list] + } + chan seek $chan $cdiroffset start + set cd [read $chan $cdirsize] + if {[string length $cd] != $cdirsize} { + error "punk::zip: central directory truncated - wanted $cdirsize bytes at $cdiroffset, got [string length $cd]" + } + set members [list] + set pos 0 + for {set i 0} {$i < $count} {incr i} { + if {$pos + 46 > $cdirsize} { + error "punk::zip: central directory truncated - record [expr {$i + 1}] of $count starts beyond the directory" + } + binary scan $cd @${pos}issssssiiisssssii sig madeby version flags method dostime dosdate crc csize size namelen extralen commentlen disknbr iattr eattr offset + if {$sig != 33639248} { + error "punk::zip: bad central directory record [expr {$i + 1}] of $count - expected signature PK\\1\\2" + } + set madeby [expr {$madeby & 0xFFFF}] + set version [expr {$version & 0xFFFF}] + set flags [expr {$flags & 0xFFFF}] + set method [expr {$method & 0xFFFF}] + set dostime [expr {$dostime & 0xFFFF}] + set dosdate [expr {$dosdate & 0xFFFF}] + set namelen [expr {$namelen & 0xFFFF}] + set extralen [expr {$extralen & 0xFFFF}] + set commentlen [expr {$commentlen & 0xFFFF}] + set iattr [expr {$iattr & 0xFFFF}] + set crc [expr {$crc & 0xFFFFFFFF}] + set csize [expr {$csize & 0xFFFFFFFF}] + set size [expr {$size & 0xFFFFFFFF}] + set eattr [expr {$eattr & 0xFFFFFFFF}] + set offset [expr {$offset & 0xFFFFFFFF}] + set namestart [expr {$pos + 46}] + set rawname [string range $cd $namestart [expr {$namestart + $namelen - 1}]] + set commentstart [expr {$namestart + $namelen + $extralen}] + set rawcomment [string range $cd $commentstart [expr {$commentstart + $commentlen - 1}]] + set packedtime [expr {($dosdate << 16) | $dostime}] + set name [Decode_text $rawname $flags] + set hostsystem [expr {($madeby >> 8) & 0xFF}] + lappend members [dict create {*}{ + } name $name {*}{ + } isdirectory [Is_directory_entry $name $size $eattr $hostsystem] {*}{ + } size $size {*}{ + } csize $csize {*}{ + } method $method {*}{ + } methodname [Method_name $method] {*}{ + } mtime [Dos_to_timet $packedtime] {*}{ + } dostime $packedtime {*}{ + } crc $crc {*}{ + } offset $offset {*}{ + } fileoffset [expr {$offsetbase + $offset}] {*}{ + } flags $flags {*}{ + } encrypted [expr {($flags & 0x41) != 0}] {*}{ + } attributes $eattr {*}{ + } iattributes $iattr {*}{ + } madeby $madeby {*}{ + } hostsystem $hostsystem {*}{ + } version $version {*}{ + } comment [Decode_text $rawcomment $flags] {*}{ + }] + incr pos [expr {46 + $namelen + $extralen + $commentlen}] + } + return $members + } + + #Member names and comments are utf-8 when general purpose bit 11 is set + #(punk::zip::mkzip always sets it), and cp437 otherwise. + proc Decode_text {bytes flags} { + if {$bytes eq ""} { + return "" + } + if {$flags & 0x800} { + return [encoding convertfrom utf-8 $bytes] + } + if {[catch {encoding convertfrom cp437 $bytes} decoded]} { + set decoded [encoding convertfrom iso8859-1 $bytes] + } + return $decoded + } + + #A trailing slash is the portable marker; the FAT directory attribute and the + #unix S_IFDIR mode bits are accepted as secondary evidence for archives that + #store directory entries without one. + proc Is_directory_entry {name size eattr hostsystem} { + if {[string index $name end] eq "/"} { + return 1 + } + if {$hostsystem == 3 && (($eattr >> 16) & 0xF000) == 0x4000} { + return 1 + } + if {$size == 0 && ($eattr & 0x10)} { + return 1 + } + return 0 + } + + punk::args::define { + @id -id ::punk::zip::Archive_read + @cmd -name punk::zip::Archive_read\ + -summary\ + "Read the structure of a zip archive on an open channel"\ + -help\ + "Derive the archive geometry and read every central directory record. + Returns a 2-element dict: 'info' (see punk::zip::archive_info for the + keys) and 'members' (see punk::zip::members). + + Never raises for a file that simply is not an archive - callers decide + what a non-ok status means. Does raise for an archive whose central + directory is truncated or malformed." + @values -min 1 -max 1 + chan -help "open channel - reconfigured to binary translation by this call" + } + proc Archive_read {chan} { + chan configure $chan -translation binary + chan seek $chan 0 end + set filesize [tell $chan] + set info [Eocd_scan $chan $filesize] + if {[dict get $info status] ne "ok"} { + #the whole file is preamble as far as any splitting caller is concerned + dict set info dataoffset $filesize + dict set info offsetstyle none + return [dict create info $info members {}] + } + set members [Cdir_records $chan $info] + set offsetbase [dict get $info offsetbase] + set minoffset "" + foreach m $members { + set o [dict get $m offset] + if {$minoffset eq "" || $o < $minoffset} { + set minoffset $o + } + } + if {$minoffset eq ""} { + set minoffset 0 + } + if {$offsetbase > 0} { + #external preamble, archive-relative offsets: the archive begins exactly + #where its own offsets say it does + set dataoffset $offsetbase + set offsetstyle archive + } else { + #file-relative offsets (or no preamble at all): the archive begins at the + #topmost local file header the directory points to. Looking at ALL records + #rather than just the first is what makes this reliable - records are in no + #guaranteed order. + set dataoffset $minoffset + set offsetstyle [expr {$minoffset > 0 ? "file" : "plain"}] + } + dict set info dataoffset $dataoffset + dict set info offsetstyle $offsetstyle + if {[llength $members]} { + #the derivation is only trustworthy if a local file header really sits + #where the first record says it does + chan seek $chan [expr {$offsetbase + $minoffset}] start + if {[read $chan 4] ne "\x50\x4b\x03\x04"} { + dict set info status unsupported + dict set info reason "no local file header at derived archive position [expr {$offsetbase + $minoffset}] - offsets do not describe this file" + } + } + return [dict create info $info members $members] + } + + #if there is an external preamble - extract that. (if there is also an internal preamble - ignore and consider part of the archive-data) + #Otherwise extract an internal preamble. + #if neither -? + #review - reconsider auto-determination of internal vs external preamble + punk::args::define { + @id -id ::punk::zip::extract_preamble + @cmd -name punk::zip::extract_preamble -help\ + "Split a zipfs based executable or library into its constituent + binary and zip parts. + + Note that the binary preamble might be either 'within' the zip offsets, + or simply catenated prior to an unadjusted zip. + Some build processes may have 'adjusted' the zip offsets to make the zip cover the entire file + ('file based' offset) whilst the more modern approach is to simply concatenate the binary and the zip + ('archive based' offset). An archive-based offset is simpler and more reliably points to the proper + split location. It also allows 'zipfs info //zipfs:/app' to return the correct offset information. + + Either way, extract_preamble can usually separate them, but in the unusual case that there is both an + external preamble and a preamble within the zip, only the external preamble will be split, with the + internal one remaining in the zip. + + The inverse of this process would be to extract the .zip file created by this split to a folder, + e.g extracted_zip_folder (adjusting contents as required) and then to run: + zipfs mkimg newbinaryname.exe extracted_zip_folder \"\" + " + @values -min 2 -max 3 + infile -type file -optional 0 -help\ + "Name of existing tcl executable or shared lib with attached zipfs filesystem" + outfile_preamble -optional 0 -type file -help\ + "Name of output file for binary preamble to be extracted to. + If this file already exists, an error will be raised" + outfile_zip -default "" -type file -help\ + "Name of output file for zip data to be extracted to. + If this file already exists, an error will be raised" + } + proc extract_preamble {args} { + set argd [punk::args::parse $args withid ::punk::zip::extract_preamble] + lassign [dict values $argd] leaders opts values received + + set infile [dict get $values infile] + set outfile_preamble [dict get $values outfile_preamble] + set outfile_zip [dict get $values outfile_zip] + + set inzip [open $infile r] + fconfigure $inzip -encoding iso8859-1 -translation binary + if {[file exists $outfile_preamble]} { + error "outfile_preamble $outfile_preamble already exists - please remove first" + } + if {$outfile_zip ne ""} { + if {[file exists $outfile_zip] && [file size $outfile_zip]} { + error "outfile_zip $outfile_zip already exists - please remove first" + } + } + chan seek $inzip 0 end + set insize [tell $inzip] ;#faster (including seeks) than calling out to filesystem using file size - but should be equivalent + + #Archive_read does the whole derivation: it finds the EOCD (rejecting the + #PK\5\6 byte sequences that occur in plain executables), decides whether the + #recorded offsets are archive-relative or file-relative, and walks ALL central + #directory records to find the topmost local file header in the file-relative + #case. dataoffset is the split point either way. + #expect CDFH PK\1\2 + #above the CD - we expect a bunch of PK\3\4 records - (possibly not all of them pointed to by the CDR) + #above that we expect: *possibly* a stored password with trailing marker - then the prefixed exe/script + set info [dict get [Archive_read $inzip] info] + switch -- [dict get $info status] { + ok { + set baseoffset [dict get $info dataoffset] + } + nozip { + #no zip eocdr - consider entire file to be the zip preamble + set baseoffset $insize + } + default { + close $inzip + error "unable to determine zip baseoffset of file $infile - [dict get $info reason]" + } + } + + if {$baseoffset < $insize} { + set pout [open $outfile_preamble w] + fconfigure $pout -encoding iso8859-1 -translation binary + chan seek $inzip 0 start + chan copy $inzip $pout -size $baseoffset + close $pout + if {$outfile_zip ne ""} { + #A file-relative archive splits into a .zip whose offsets still count + #from the removed preamble - readers that only accept a bare .zip choke + #on it. punk::zip's own reader never needs the split: point it at the + #ORIGINAL file and the derived base offset makes the shape irrelevant. + #Rewriting the offsets here stays open for callers that must hand a + #plain .zip to something else. + set zout [open $outfile_zip w] + fconfigure $zout -encoding iso8859-1 -translation binary + chan copy $inzip $zout + close $zout + } + close $inzip + } else { + #no valid (from our perspective) eocdr found - baseoffset has been set to insize + close $inzip + file copy $infile $outfile_preamble + if {$outfile_zip ne ""} { + #touch equiv? + set fd [open $outfile_zip w] + close $fd + } + } + } + + # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + # Reading - archive_info / members / unzip + # + # Stock Tcl only: no zipfs, no vfs::zip, no tcllib. Works on a plain .zip and on + # a zip attached to an executable or script prefix, under either offset + # convention, because everything reads the ORIGINAL file at a derived base + # offset rather than a split-off intermediate. + # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + + #open + read structure + close, for the surfaces that take a filename + proc Open_archive {zipfile requirezip} { + if {![file exists $zipfile]} { + error "punk::zip: no such file '$zipfile'" + } + set chan [open $zipfile rb] + if {[catch {Archive_read $chan} arc erropts]} { + close $chan + return -options $erropts $arc + } + set info [dict get $arc info] + set status [dict get $info status] + if {$requirezip && $status ne "ok"} { + close $chan + switch -- $status { + nozip { + error "punk::zip: '$zipfile' is not a zip archive - [dict get $info reason]" + } + default { + error "punk::zip: cannot read '$zipfile' - [dict get $info reason]" + } + } + } + return [dict set arc chan $chan] + } + + #glob selection shared by members and unzip. Patterns are matched against the + #member name both as stored and with any trailing slash removed, so a pattern + #like lib/* selects the lib/ directory entry as well as its contents. + proc Select_members {members globs excludes} { + set selected [list] + foreach m $members { + set name [dict get $m name] + set bare [string trimright $name /] + set matched 0 + foreach g $globs { + if {[string match $g $name] || [string match $g $bare]} { + set matched 1 + break + } + } + if {!$matched} { + continue + } + set excluded 0 + foreach e $excludes { + if {[string match $e $name] || [string match $e $bare]} { + set excluded 1 + break + } + } + if {!$excluded} { + lappend selected $m + } + } + return $selected + } + + #Member names are archive data, not trusted paths: a name that escapes the target + #directory is refused rather than followed. Backslashes are normalised to forward + #slashes (some writers emit them) so the check cannot be side-stepped on windows. + proc Safe_member_path {name} { + set segments [list] + foreach seg [split [string map {\\ /} $name] /] { + if {$seg eq "" || $seg eq "."} { + continue + } + if {$seg eq ".."} { + error "punk::zip: refusing member '$name' - parent-directory segment would escape the target directory" + } + lappend segments $seg + } + if {![llength $segments]} { + error "punk::zip: refusing member '$name' - empty path" + } + if {[file pathtype [lindex $segments 0]] ne "relative"} { + error "punk::zip: refusing member '$name' - not a relative path" + } + return $segments + } + + #Reasons punk::zip cannot extract a member. Checked for every selected member + #BEFORE anything is written, so an unsupported archive produces an error instead + #of a partly-populated target directory. + proc Member_unsupported_reason {m} { + if {[dict get $m encrypted]} { + return "entry is encrypted - punk::zip cannot decrypt zip entries" + } + if {[dict get $m size] == 0xFFFFFFFF || [dict get $m csize] == 0xFFFFFFFF || [dict get $m offset] == 0xFFFFFFFF} { + return "entry uses zip64 size/offset fields - punk::zip reads zip32 entries only" + } + set method [dict get $m method] + if {$method ni {0 8}} { + return "entry uses compression method $method ([dict get $m methodname]) - punk::zip reads stored (0) and deflated (8) entries only" + } + return "" + } + + #Read one member's data from the archive and write it to target. + #Sizes and offsets come from the central directory, so entries written with a + #data descriptor (streamed, sizes zero in the local header) read correctly. + proc Extract_member {chan m target verify} { + set name [dict get $m name] + set fileoffset [dict get $m fileoffset] + set csize [dict get $m csize] + set size [dict get $m size] + set method [dict get $m method] + chan seek $chan $fileoffset start + set lfh [read $chan 30] + if {[string length $lfh] != 30} { + error "punk::zip: truncated local file header for member '$name' at offset $fileoffset" + } + binary scan $lfh isssssiiiss lsig lversion lflags lmethod ltime ldate lcrc lcsize lsize lnamelen lextralen + #dec2hex 67324752 = 4034B50 = PK\3\4 + if {$lsig != 67324752} { + error "punk::zip: no local file header for member '$name' at offset $fileoffset - archive offsets do not describe this file" + } + set lnamelen [expr {$lnamelen & 0xFFFF}] + set lextralen [expr {$lextralen & 0xFFFF}] + chan seek $chan [expr {$fileoffset + 30 + $lnamelen + $lextralen}] start + set out [open $target wb] + set crc 0 + set written 0 + try { + if {$size < 0x00200000} { + #small member - one chunk, mirroring Addentry's 2MB write threshold + set data "" + if {$csize > 0} { + set data [read $chan $csize] + if {[string length $data] != $csize} { + error "punk::zip: truncated data for member '$name' - wanted $csize bytes, got [string length $data]" + } + if {$method == 8} { + set data [zlib inflate $data] + } + } + set crc [zlib crc32 $data] + set written [string length $data] + puts -nonewline $out $data + } else { + #large member - stream, to avoid holding the whole thing in memory + set strm "" + if {$method == 8} { + set strm [zlib stream inflate] + } + set remaining $csize + while {$remaining > 0} { + set want [expr {$remaining > 65536 ? 65536 : $remaining}] + set chunk [read $chan $want] + if {[string length $chunk] != $want} { + error "punk::zip: truncated data for member '$name' - archive ends mid-entry" + } + incr remaining -$want + if {$strm eq ""} { + set crc [zlib crc32 $chunk $crc] + incr written [string length $chunk] + puts -nonewline $out $chunk + continue + } + if {$remaining == 0} { + $strm put -finalize $chunk + } else { + $strm put $chunk + } + #a single get returns only what the stream has already produced - + #drain until empty or the tail of the member is silently dropped + while {1} { + set plain [$strm get 65536] + if {$plain eq ""} { + break + } + set crc [zlib crc32 $plain $crc] + incr written [string length $plain] + puts -nonewline $out $plain + } + } + if {$strm ne ""} { + $strm close + } + } + close $out + set out "" + if {$written != $size} { + error "punk::zip: size mismatch for member '$name' - central directory says $size bytes, decompressed to $written" + } + if {$verify && ($crc & 0xFFFFFFFF) != [dict get $m crc]} { + error "punk::zip: crc mismatch for member '$name' - archive records [dict get $m crc], extracted data is [expr {$crc & 0xFFFFFFFF}]" + } + } on error {result erropts} { + if {$out ne ""} { + catch {close $out} + } + #never leave unverified bytes behind + catch {file delete -- $target} + return -options $erropts $result + } + return $written + } + + punk::args::define { + @id -id ::punk::zip::archive_info + @cmd -name punk::zip::archive_info\ + -summary\ + "Report the zip-archive geometry of a file"\ + -help\ + "Report where the zip archive inside a file begins and which offset + convention it uses, without extracting anything. + + The file may be a plain .zip or a zip attached to an executable or script + prefix - a punk kit, a tclsh carrying a zipfs image, a zip-based .tm + modpod. This is the derivation punk::zip::members, punk::zip::unzip and + punk::zip::extract_preamble all share. + + Does not raise for a file that simply is not an archive - read 'status'. + + Returned dict keys: + status ok | nozip | unsupported + reason why status is not ok + filesize size of the whole file + offsetstyle plain | archive | file | none + plain - a bare zip, no prefix + archive - prefixed, offsets counted from the zip + file - prefixed, offsets counted from the file + none - no archive found + dataoffset where the zip data starts, ie the prefix length + offsetbase add this to a member's stored offset for a file position + eocdoffset file position of the end-of-central-directory record + cdiroffset file position of the central directory + cdirsize size of the central directory + count number of members + diroffset central directory offset as recorded in the archive + comment archive comment + + Examples: + #which convention is this runtime using? + dict get [punk::zip::archive_info bin/punk91.exe] offsetstyle + + #how big is the executable prefix? + dict get [punk::zip::archive_info bin/punk91.exe] dataoffset + + #is there an archive attached at all? (status is ok when there is) + dict get [punk::zip::archive_info some.exe] status + " + @values -min 1 -max 1 + zipfile -type existingfile -help\ + "Path of a zip archive, or of a file with one attached" + } + proc archive_info {args} { + set argd [punk::args::parse $args withid ::punk::zip::archive_info] + set zipfile [dict get $argd values zipfile] + set arc [Open_archive $zipfile 0] + close [dict get $arc chan] + return [dict get $arc info] + } + + punk::args::define { + @id -id ::punk::zip::members + @cmd -name punk::zip::members\ + -summary\ + "List the members of a zip archive without extracting"\ + -help\ + "Read the central directory of a zip archive and return one dict per + member, in stored order, without decompressing anything. + + The archive may be a plain .zip or a zip attached to an executable or + script prefix - see punk::zip::archive_info. Requires only stock Tcl: no + zipfs, no vfs::zip, no tcllib. + + Entries punk::zip cannot EXTRACT (encrypted, zip64, an unsupported + compression method) are still listed - the refusal belongs to + punk::zip::unzip, which names the reason. + + Each member dict carries: + name member path as stored (directories keep a trailing /) + isdirectory 1 for a directory entry, else 0 + size uncompressed size in bytes + csize compressed size in bytes + method compression method id (0 store, 8 deflate) + methodname symbolic name for that method + mtime modification time as a unix timestamp (0 if unusable) + dostime the packed DOS date/time exactly as stored + crc crc32 of the uncompressed data + offset local header offset as stored in the central directory + fileoffset absolute position of that local header in the file + flags general purpose bit flag + encrypted 1 if the entry is encrypted + attributes external file attributes as stored + iattributes internal file attributes as stored + madeby version-made-by field + hostsystem host system code (upper byte of madeby - 0 fat, 3 unix) + version version needed to extract + comment per-member comment + + Examples: + #everything inside a kit + punk::zip::members bin/punk91.exe + + #just the tcl_library scripts + punk::zip::members bin/punk91.exe tcl_library/*.tcl + + #total uncompressed size + set bytes 0 + foreach m [punk::zip::members some.zip] {incr bytes [dict get \$m size]} + + #names only, files not directories + set names [list] + foreach m [punk::zip::members some.zip] { + if {![dict get \$m isdirectory]} {lappend names [dict get \$m name]} + } + " + @opts + -exclude -default {} -help\ + "List of glob expressions matched against member names. + A member matching any of them is left out of the result." + -- -type none -optional 1 -help\ + "End of options marker" + @values -min 1 -max -1 + zipfile -type existingfile -help\ + "Path of a zip archive, or of a file with one attached" + globs -default {*} -multiple 1 -help\ + "Glob patterns matched against member names with 'string match'. + A member is included if it matches any of them. + The whole stored name is matched, so * spans path separators: + *.txt selects sub/deeper/notes.txt as well as top.txt. + Directory entries match with or without their trailing slash." + } + proc members {args} { + set argd [punk::args::parse $args withid ::punk::zip::members] + set zipfile [dict get $argd values zipfile] + set globs [dict get $argd values globs] + set excludes [dict get $argd opts -exclude] + set arc [Open_archive $zipfile 1] + close [dict get $arc chan] + return [Select_members [dict get $arc members] $globs $excludes] + } + + punk::args::define { + @id -id ::punk::zip::unzip + @cmd -name punk::zip::unzip\ + -summary\ + "Extract a zip archive to a directory"\ + -help\ + "Extract all or part of a zip archive into targetdir, verifying each + member's crc32 as it is written. Directory entries are created as + directories; intermediate directories of a selected file are created + whether or not the archive stores an entry for them. + + The archive may be a plain .zip or a zip attached to an executable or + script prefix - see punk::zip::archive_info. Requires only stock Tcl: no + zipfs, no vfs::zip, no tcllib. + + Every selected member is checked for extractability BEFORE anything is + written, so an encrypted, zip64 or unknown-compression archive fails + naming the reason instead of leaving a half-populated directory. A + member whose path would escape targetdir is refused the same way. + + Returns the list of member names extracted (see -return). + + Examples: + #whole archive + punk::zip::unzip some.zip /tmp/out + + #lift a runtime's tcl_library out of the executable it is attached to + punk::zip::unzip bin/punk91.exe /tmp/rt tcl_library/* + + #everything except the docs, keeping timestamps + punk::zip::unzip -exclude {doc/*} -- some.zip /tmp/out + " + @opts + -exclude -default {} -help\ + "List of glob expressions matched against member names. + A member matching any of them is not extracted." + -overwrite -default 1 -type boolean -help\ + "Whether an existing file in targetdir may be replaced. + With -overwrite 0 an existing target is an error, raised before + anything is written." + -mtime -default 1 -type boolean -help\ + "Whether to restore each member's stored modification time. + Directory times are applied after their contents are written." + -verify -default 1 -type boolean -help\ + "Whether to check each extracted member against the crc32 stored in + the archive. A mismatch is an error and the partial file is removed." + -return -default list -choices {list pretty none} -help\ + "list - return the extracted member names (default) + pretty - format that list for terminal display + none - return the empty string" + -- -type none -optional 1 -help\ + "End of options marker" + @values -min 2 -max -1 + zipfile -type existingfile -help\ + "Path of a zip archive, or of a file with one attached" + targetdir -type directory -help\ + "Directory to extract into. Created if it does not exist." + globs -default {*} -multiple 1 -help\ + "Glob patterns matched against member names with 'string match'. + A member is extracted if it matches any of them. + The whole stored name is matched, so * spans path separators: + *.txt selects sub/deeper/notes.txt as well as top.txt. + Directory entries match with or without their trailing slash." + } + proc unzip {args} { + set argd [punk::args::parse $args withid ::punk::zip::unzip] + set zipfile [dict get $argd values zipfile] + set targetdir [dict get $argd values targetdir] + set globs [dict get $argd values globs] + set excludes [dict get $argd opts -exclude] + set overwrite [dict get $argd opts -overwrite] + set restoremtime [dict get $argd opts -mtime] + set verify [dict get $argd opts -verify] + + set arc [Open_archive $zipfile 1] + set chan [dict get $arc chan] + set extracted [list] + set dirtimes [list] + try { + set selected [Select_members [dict get $arc members] $globs $excludes] + #preflight - nothing is written until every selected member is known good + set targets [list] + foreach m $selected { + set reason [Member_unsupported_reason $m] + if {$reason ne ""} { + error "punk::zip::unzip: cannot extract '[dict get $m name]' from $zipfile - $reason" + } + set target [file join $targetdir {*}[Safe_member_path [dict get $m name]]] + if {!$overwrite && !([dict get $m isdirectory]) && [file exists $target]} { + error "punk::zip::unzip: '$target' already exists and -overwrite is 0" + } + lappend targets $target + } + file mkdir $targetdir + foreach m $selected target $targets { + set name [dict get $m name] + if {[dict get $m isdirectory]} { + file mkdir $target + if {$restoremtime && [dict get $m mtime]} { + lappend dirtimes $target [dict get $m mtime] + } + } else { + file mkdir [file dirname $target] + Extract_member $chan $m $target $verify + if {$restoremtime && [dict get $m mtime]} { + catch {file mtime $target [dict get $m mtime]} + } + } + lappend extracted $name + } + } finally { + close $chan + } + #directory times last - writing their contents would have reset them + foreach {dir mtime} $dirtimes { + catch {file mtime $dir $mtime} + } + + switch -exact -- [dict get $argd opts -return] { + pretty { + if {[info commands showlist] ne ""} { + return [plist -channel none extracted] + } + return $extracted + } + none { + return "" + } + default { + return $extracted + } + } + } + + + + punk::args::define { + @id -id ::punk::zip::Addentry + @cmd -name punk::zip::Addentry\ + -summary\ + "Add zip-entry for file at 'path'"\ + -help\ + "Add a single file at 'path' to open channel 'zipchan' + return a central directory file record" + @opts + -comment -default "" -help "An optional comment specific to the added file" + @values -min 3 -max 4 + zipchan -help "open file descriptor with cursor at position appropriate for writing a local file header" + base -help "base path for entries" + path -type file -help "path of file to add" + zipdataoffset -default 0 -type integer -range {0 ""} -help "offset of start of zip-data - ie length of prefixing script/exe + Can be specified as zero even if a prefix exists - which would make offsets 'file relative' as opposed to 'archive relative'" + } + + # Addentry - was Mkzipfile -- + # + # FIX ME: should handle the current offset for non-seekable channels + # + proc Addentry {args} { + #*** !doctools + #[call [fun Addentry] [arg zipchan] [arg base] [arg path] [arg ?comment?]] + #[para] Add a single file to a zip archive + #[para] The zipchan channel should already be open and binary. + #[para] You can provide a -comment for the file. + #[para] The return value is the central directory record that will need to be used when finalizing the zip archive. + + set argd [punk::args::parse $args withid ::punk::zip::Addentry] + set zipchan [dict get $argd values zipchan] + set base [dict get $argd values base] + set path [dict get $argd values path] + set zipdataoffset [dict get $argd values zipdataoffset] + + set comment [dict get $argd opts -comment] + + set fullpath [file join $base $path] + set mtime [Timet_to_dos [file mtime $fullpath]] + set utfpath [encoding convertto utf-8 $path] + set utfcomment [encoding convertto utf-8 $comment] + set flags [expr {(1<<11)}] ;# utf-8 comment and path + set method 0 ;# store 0, deflate 8 + set attr 0 ;# text or binary (default binary) + set version 20 ;# minumum version req'd to extract + set extra "" + set crc 0 + set size 0 + set csize 0 + set data "" + set seekable [expr {[tell $zipchan] != -1}] + if {[file isdirectory $fullpath]} { + set attrex 0x41ff0010 ;# 0o040777 (drwxrwxrwx) + #set attrex 0x40000010 + } elseif {[file executable $fullpath]} { + set attrex 0x81ff0080 ;# 0o100777 (-rwxrwxrwx) + } else { + set attrex 0x81b60020 ;# 0o100666 (-rw-rw-rw-) + if {[file extension $fullpath] in {".tcl" ".txt" ".c"}} { + set attr 1 ;# text + } + } + + if {[file isfile $fullpath]} { + set size [file size $fullpath] + if {!$seekable} {set flags [expr {$flags | (1 << 3)}]} + } + + + set channeloffset [tell $zipchan] ;#position in the channel - this may include prefixing exe/zip + set local [binary format a4sssiiiiss PK\03\04 \ + $version $flags $method $mtime $crc $csize $size \ + [string length $utfpath] [string length $extra]] + append local $utfpath $extra + puts -nonewline $zipchan $local + + if {[file isfile $fullpath]} { + # If the file is under 2MB then zip in one chunk, otherwize we use + # streaming to avoid requiring excess memory. This helps to prevent + # storing re-compressed data that may be larger than the source when + # handling PNG or JPEG or nested ZIP files. + if {$size < 0x00200000} { + set fin [open $fullpath rb] + set data [read $fin] + set crc [zlib crc32 $data] + set cdata [zlib deflate $data] + if {[string length $cdata] < $size} { + set method 8 + set data $cdata + } + close $fin + set csize [string length $data] + puts -nonewline $zipchan $data + } else { + set method 8 + set fin [open $fullpath rb] + set zlib [zlib stream deflate] + while {![eof $fin]} { + set data [read $fin 4096] + set crc [zlib crc32 $data $crc] + $zlib put $data + if {[string length [set zdata [$zlib get]]]} { + incr csize [string length $zdata] + puts -nonewline $zipchan $zdata + } + } + close $fin + $zlib finalize + set zdata [$zlib get] + incr csize [string length $zdata] + puts -nonewline $zipchan $zdata + $zlib close + } + + if {$seekable} { + # update the header if the output is seekable + set local [binary format a4sssiiii PK\03\04 \ + $version $flags $method $mtime $crc $csize $size] + set current [tell $zipchan] + seek $zipchan $channeloffset + puts -nonewline $zipchan $local + seek $zipchan $current + } else { + # Write a data descriptor record + set ddesc [binary format a4iii PK\7\8 $crc $csize $size] + puts -nonewline $zipchan $ddesc + } + } + + #PK\x01\x02 Cdentral directory file header + #set v1 0x0317 ;#upper byte 03 -> UNIX lower byte 23 -> 2.3 + set v1 0x0017 ;#upper byte 00 -> MS_DOS and OS/2 (FAT/VFAT/FAT32 file systems) + + set hdr [binary format a4ssssiiiisssssii PK\01\02 $v1 \ + $version $flags $method $mtime $crc $csize $size \ + [string length $utfpath] [string length $extra]\ + [string length $utfcomment] 0 $attr $attrex [expr {$channeloffset - $zipdataoffset}]] ;#zipdataoffset may be zero - either because it's a pure zip, or file-based offsets desired. + append hdr $utfpath $extra $utfcomment + return $hdr + } + + #### REVIEW!!! + #JMN - review - this looks to be offset relative to start of file - (same as 2024 Tcl 'mkzip mkimg') + # we want to enable (optionally) offsets relative to start of archive for exe/script-prefixed zips.on windows (editability with 7z,peazip) + #### + + + punk::args::define { + @id -id ::punk::zip::mkzip + @cmd -name punk::zip::mkzip\ + -summary\ + "Create a zip archive in 'filename'."\ + -help\ + "Create a zip archive in 'filename'" + @opts + -offsettype -default "archive" -choices {archive file}\ + -help\ + "zip offsets stored relative to start of entire file or relative to start of zip-archive + Only relevant if the created file has a script/runtime prefix." + -return -default "pretty" -choices {pretty list none}\ + -help\ + "mkzip can return a list of the files and folders added to the archive + the option -return pretty is the default and uses the punk::lib pdict/plist system + to return a formatted list for the terminal + " + -zipkit -default 0 -type none\ + -help\ + "whether to add mounting script + mutually exclusive with -runtime option + currently vfs::zip based - todo - autodetect zipfs/vfs with pref for zipfs" + -runtime -default ""\ + -help\ + "specify a prefix file + e.g punk::zip::mkzip -runtime unzipsfx.exe -directory subdir -base subdir output.zip + will create a self-extracting zip archive from the subdir/ folder. + Expects runtime with no existing vfs attached (review)" + -comment -default ""\ + -help "An optional comment for the archive" + -directory -default ""\ + -help "Scan for contents within this folder or current directory if not provided." + -base -default ""\ + -help\ + "The new zip archive will be rooted in this directory if provided + it must be a parent of -directory or the same path as -directory" + -exclude -default {CVS/* */CVS/* *~ ".#*" "*/.#*"} + -- -type none -help\ + "End of options marker" + + @values -min 1 -max -1 + filename -type file -default ""\ + -help "name of zipfile to create" + globs -default {*} -multiple 1\ + -help\ + "list of glob patterns to match. + Only directories with matching files will be included in the archive." + } + + # zip::mkzip -- + # + # eg: zip my.zip -directory Subdir -runtime unzipsfx.exe *.txt + # + proc mkzip {args} { + #todo - doctools - [arg ?globs...?] syntax? + + #*** !doctools + #[call [fun mkzip]\ + # [opt "[option -offsettype] [arg offsettype]"]\ + # [opt "[option -return] [arg returntype]"]\ + # [opt "[option -zipkit] [arg 0|1]"]\ + # [opt "[option -runtime] [arg preamble_filename]"]\ + # [opt "[option -comment] [arg zipfilecomment]"]\ + # [opt "[option -directory] [arg dir_to_zip]"]\ + # [opt "[option -base] [arg archive_root]"]\ + # [opt "[option -exclude] [arg globlist]"]\ + # [arg zipfilename]\ + # [arg ?glob...?]] + #[para] Create a zip archive in 'zipfilename' + #[para] If a file already exists, an error will be raised. + #[para] Call 'punk::zip::mkzip' with no arguments for usage display. + + set argd [punk::args::parse $args withid ::punk::zip::mkzip] + set filename [dict get $argd values filename] + if {$filename eq ""} { + error "mkzip filename cannot be empty string" + } + if {[regexp {[?*]} $filename]} { + #catch a likely error where filename is omitted and first glob pattern is misinterpreted as zipfile name + error "mkzip filename should not contain glob characters ? *" + } + if {[file exists $filename]} { + error "mkzip filename:$filename already exists" + } + dict for {k v} [dict get $argd opts] { + switch -- $k { + -comment { + dict set argd opts $k [encoding convertto utf-8 $v] + } + -directory - -base { + dict set argd opts $k [file normalize $v] + } + } + } + + array set opts [dict get $argd opts] + + + if {$opts(-directory) ne ""} { + if {$opts(-base) ne ""} { + #-base and -directory have been normalized already + if {![Path_a_atorbelow_b $opts(-directory) $opts(-base)]} { + error "punk::zip::mkzip -base $opts(-base) must be above or the same as -directory $opts(-directory)" + } + set base $opts(-base) + set relpath [Path_strip_alreadynormalized_prefixdepth $opts(-directory) $opts(-base)] + } else { + set base $opts(-directory) + set relpath "" + } + #will pick up intermediary folders as paths (ending with trailing slash) + set paths [walk -exclude $opts(-exclude) -subpath $relpath -- $base {*}[dict get $argd values globs]] + + set norm_filename [file normalize $filename] + set norm_dir [file normalize $opts(-directory)] ;#we only care if filename below -directory (which is where we start scanning) + if {[Path_a_atorbelow_b $norm_filename $norm_dir]} { + #check that we aren't adding the zipfile to itself + #REVIEW - now that we open zipfile after scanning - this isn't really a concern! + #keep for now in case we can add an -update or a -force facility (or in case we modify to add to zip as we scan for members?) + #In the case of -force - we may want to delay replacement of original until scan is done? + + #try to avoid looping on all paths and performing (somewhat) expensive file normalizations on each + #1st step is to check the patterns and see if our zipfile is already excluded - in which case we need not check the paths + set self_globs_match 0 + foreach g [dict get $argd values globs] { + if {[string match $g [file tail $filename]]} { + set self_globs_match 1 + break + } + } + if {$self_globs_match} { + #still dangerous + set self_excluded 0 + foreach e $opts(-exclude) { + if {[string match $e [file tail $filename]]} { + set self_excluded 1 + break + } + } + if {!$self_excluded} { + #still dangerous - likely to be in resultset - check each path + #puts stderr "zip file $filename is below directory $opts(-directory)" + set self_is_matched 0 + set i 0 + foreach p $paths { + set norm_p [file normalize [file join $opts(-directory) $p]] + if {[Path_a_at_b $norm_filename $norm_p]} { + set self_is_matched 1 + break + } + incr i + } + if {$self_is_matched} { + puts stderr "WARNING - zipfile being created '$filename' was matched. Excluding this file. Relocate the zip, or use -exclude patterns to avoid this message" + set paths [lremove $paths $i] + } + } + } + } + } else { + #NOTE that we don't add intermediate folders when creating an archive without using the -directory flag! + #ie - only the exact *files* matching the glob are stored. + set paths [list] + set dir [pwd] + if {$opts(-base) ne ""} { + if {![Path_a_atorbelow_b $dir $opts(-base)]} { + error "punk::zip::mkzip -base $opts(-base) must be above current directory" + } + set relpath [Path_strip_alreadynormalized_prefixdepth [file normalize $dir] [file normalize $opts(-base)]] + } else { + set relpath "" + } + set base $opts(-base) + + set matches [glob -nocomplain -type f -- {*}[dict get $argd values globs]] + foreach m $matches { + if {$m eq $filename} { + #puts stderr "--> excluding $filename" + continue + } + set isok 1 + foreach e [concat $opts(-exclude) $filename] { + if {[string match $e $m]} { + set isok 0 + break + } + } + if {$isok} { + lappend paths [file join $relpath $m] + } + } + } + + if {![llength $paths]} { + return "" + } + + set zf [open $filename wb] + if {$opts(-runtime) ne ""} { + #todo - strip any existing vfs - option to merge contents.. only if zip attached? + set rt [open $opts(-runtime) rb] + fcopy $rt $zf + close $rt + } elseif {$opts(-zipkit)} { + #TODO - update to zipfs ? + #see modpod + set zkd "#!/usr/bin/env tclkit\n\# This is a zip-based Tcl Module\n" + append zkd "package require vfs::zip\n" + append zkd "vfs::zip::Mount \[info script\] \[info script\]\n" + append zkd "if {\[file exists \[file join \[info script\] main.tcl\]\]} {\n" + append zkd " source \[file join \[info script\] main.tcl\]\n" + append zkd "}\n" + append zkd \x1A + puts -nonewline $zf $zkd + } + + #todo - subtract this from the endrec offset + if {$opts(-offsettype) eq "archive"} { + set dataStartOffset [tell $zf] ;#the overall file offset of the start of archive-data //JMN 2024 + } else { + set dataStartOffset 0 ;#offsets relative to file - the (old) zipfs mkzip way :/ + } + + set count 0 + set cd "" + + set members [list] + foreach path $paths { + #puts $path + lappend members $path + append cd [Addentry $zf $base $path $dataStartOffset] ;#path already includes relpath + incr count + } + set cdoffset [tell $zf] + set endrec [binary format a4ssssiis PK\05\06 0 0 \ + $count $count [string length $cd] [expr {$cdoffset - $dataStartOffset}]\ + [string length $opts(-comment)]] + append endrec $opts(-comment) + puts -nonewline $zf $cd + puts -nonewline $zf $endrec + close $zf + + set result "" + switch -exact -- $opts(-return) { + list { + set result $members + } + pretty { + if {[info commands showlist] ne ""} { + set result [plist -channel none members] + } else { + set result $members + } + } + none { + set result "" + } + } + return $result + } + + + #*** !doctools + #[list_end] [comment {--- end definitions namespace punk::zip ---}] +} +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# Secondary API namespace +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +tcl::namespace::eval punk::zip::lib { + tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase + tcl::namespace::path [tcl::namespace::parent] + #*** !doctools + #[subsection {Namespace punk::zip::lib}] + #[para] Secondary functions that are part of the API + #[list_begin definitions] + + #proc utility1 {p1 args} { + # #*** !doctools + # #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]] + # #[para]Description of utility1 + # return 1 + #} + + + + #*** !doctools + #[list_end] [comment {--- end definitions namespace punk::zip::lib ---}] +} +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +## Ready +package provide punk::zip [tcl::namespace::eval punk::zip { + variable pkg punk::zip + variable version + set version 0.2.0 +}] +return + +#*** !doctools +#[manpage_end] + diff --git a/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl b/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl index 7d08906c..68653cf4 100644 --- a/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl +++ b/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl @@ -5937,20 +5937,22 @@ foreach vfstail $vfs_tails { file copy -force $building_runtime $raw_runtime } } else { - #The driving tcl we are calling with doesn't have zipfs - can't mount + #The driving tcl we are calling with doesn't have zipfs - can't mount. + #punk::zip reads the attached archive with stock Tcl only (G-124), + #so this path needs no zipfs, no vfs::zip and no tcllib. package require punk::zip - puts stderr "WARNING: tcl shell '[info nameofexecutable]' being used to build doesn't have zipfs - falling back to punk::zip::extract_preamble" - set extractedzipfile $buildfolder/extracted_$runtime_fullname.zip + puts stdout "tcl shell '[info nameofexecutable]' being used to build doesn't have zipfs - reading the runtime's attached archive with punk::zip" set extractedzipfolder $buildfolder/extracted_$runtime_fullname file delete $raw_runtime - file delete $extractedzipfile file delete -force $extractedzipfolder if {![catch { - punk::zip::extract_preamble $building_runtime $raw_runtime $extractedzipfile - package require zipfile::decode - zipfile::decode::open $extractedzipfile - set archiveinfo [zipfile::decode::archive] - zipfile::decode::unzip $archiveinfo $extractedzipfolder + #Split off the executable prefix - the kit assembly needs it as a + #file. The MEMBERS are then read from the ORIGINAL runtime at its + #derived base offset: a runtime whose offsets are file-relative + #splits into a .zip no plain zip reader accepts, and reading the + #whole file sidesteps that intermediate entirely. + punk::zip::extract_preamble $building_runtime $raw_runtime + punk::zip::unzip -return none -- $building_runtime $extractedzipfolder } extracterr]} { set extraction_done 1 set extract_kit_type $extract_kit_try @@ -5958,10 +5960,7 @@ foreach vfstail $vfs_tails { merge_over $extractedzipfolder $targetvfs } else { #Do not swallow the reason: without the runtime's own - #zip contents the built kit has no tcl_library. Note - #zipfile::decode is a system tcllib package, not a - #vendored one - a tclsh without tcllib (e.g the msys2 - #/usr/bin/tclsh8.6) cannot take this path at all. + #zip contents the built kit has no tcl_library. puts stderr "zipfs-less extraction of $runtime_fullname FAILED: $extracterr" } } diff --git a/src/project_layouts/vendor/punk/basic/src/make.tcl b/src/project_layouts/vendor/punk/basic/src/make.tcl index 7d08906c..68653cf4 100644 --- a/src/project_layouts/vendor/punk/basic/src/make.tcl +++ b/src/project_layouts/vendor/punk/basic/src/make.tcl @@ -5937,20 +5937,22 @@ foreach vfstail $vfs_tails { file copy -force $building_runtime $raw_runtime } } else { - #The driving tcl we are calling with doesn't have zipfs - can't mount + #The driving tcl we are calling with doesn't have zipfs - can't mount. + #punk::zip reads the attached archive with stock Tcl only (G-124), + #so this path needs no zipfs, no vfs::zip and no tcllib. package require punk::zip - puts stderr "WARNING: tcl shell '[info nameofexecutable]' being used to build doesn't have zipfs - falling back to punk::zip::extract_preamble" - set extractedzipfile $buildfolder/extracted_$runtime_fullname.zip + puts stdout "tcl shell '[info nameofexecutable]' being used to build doesn't have zipfs - reading the runtime's attached archive with punk::zip" set extractedzipfolder $buildfolder/extracted_$runtime_fullname file delete $raw_runtime - file delete $extractedzipfile file delete -force $extractedzipfolder if {![catch { - punk::zip::extract_preamble $building_runtime $raw_runtime $extractedzipfile - package require zipfile::decode - zipfile::decode::open $extractedzipfile - set archiveinfo [zipfile::decode::archive] - zipfile::decode::unzip $archiveinfo $extractedzipfolder + #Split off the executable prefix - the kit assembly needs it as a + #file. The MEMBERS are then read from the ORIGINAL runtime at its + #derived base offset: a runtime whose offsets are file-relative + #splits into a .zip no plain zip reader accepts, and reading the + #whole file sidesteps that intermediate entirely. + punk::zip::extract_preamble $building_runtime $raw_runtime + punk::zip::unzip -return none -- $building_runtime $extractedzipfolder } extracterr]} { set extraction_done 1 set extract_kit_type $extract_kit_try @@ -5958,10 +5960,7 @@ foreach vfstail $vfs_tails { merge_over $extractedzipfolder $targetvfs } else { #Do not swallow the reason: without the runtime's own - #zip contents the built kit has no tcl_library. Note - #zipfile::decode is a system tcllib package, not a - #vendored one - a tclsh without tcllib (e.g the msys2 - #/usr/bin/tclsh8.6) cannot take this path at all. + #zip contents the built kit has no tcl_library. puts stderr "zipfs-less extraction of $runtime_fullname FAILED: $extracterr" } } diff --git a/src/project_layouts/vendor/punk/project-0.1/src/make.tcl b/src/project_layouts/vendor/punk/project-0.1/src/make.tcl index 7d08906c..68653cf4 100644 --- a/src/project_layouts/vendor/punk/project-0.1/src/make.tcl +++ b/src/project_layouts/vendor/punk/project-0.1/src/make.tcl @@ -5937,20 +5937,22 @@ foreach vfstail $vfs_tails { file copy -force $building_runtime $raw_runtime } } else { - #The driving tcl we are calling with doesn't have zipfs - can't mount + #The driving tcl we are calling with doesn't have zipfs - can't mount. + #punk::zip reads the attached archive with stock Tcl only (G-124), + #so this path needs no zipfs, no vfs::zip and no tcllib. package require punk::zip - puts stderr "WARNING: tcl shell '[info nameofexecutable]' being used to build doesn't have zipfs - falling back to punk::zip::extract_preamble" - set extractedzipfile $buildfolder/extracted_$runtime_fullname.zip + puts stdout "tcl shell '[info nameofexecutable]' being used to build doesn't have zipfs - reading the runtime's attached archive with punk::zip" set extractedzipfolder $buildfolder/extracted_$runtime_fullname file delete $raw_runtime - file delete $extractedzipfile file delete -force $extractedzipfolder if {![catch { - punk::zip::extract_preamble $building_runtime $raw_runtime $extractedzipfile - package require zipfile::decode - zipfile::decode::open $extractedzipfile - set archiveinfo [zipfile::decode::archive] - zipfile::decode::unzip $archiveinfo $extractedzipfolder + #Split off the executable prefix - the kit assembly needs it as a + #file. The MEMBERS are then read from the ORIGINAL runtime at its + #derived base offset: a runtime whose offsets are file-relative + #splits into a .zip no plain zip reader accepts, and reading the + #whole file sidesteps that intermediate entirely. + punk::zip::extract_preamble $building_runtime $raw_runtime + punk::zip::unzip -return none -- $building_runtime $extractedzipfolder } extracterr]} { set extraction_done 1 set extract_kit_type $extract_kit_try @@ -5958,10 +5960,7 @@ foreach vfstail $vfs_tails { merge_over $extractedzipfolder $targetvfs } else { #Do not swallow the reason: without the runtime's own - #zip contents the built kit has no tcl_library. Note - #zipfile::decode is a system tcllib package, not a - #vendored one - a tclsh without tcllib (e.g the msys2 - #/usr/bin/tclsh8.6) cannot take this path at all. + #zip contents the built kit has no tcl_library. puts stderr "zipfs-less extraction of $runtime_fullname FAILED: $extracterr" } } diff --git a/src/vfs/_vfscommon.vfs/modules/punk-0.2.7.tm b/src/vfs/_vfscommon.vfs/modules/punk-0.2.8.tm similarity index 99% rename from src/vfs/_vfscommon.vfs/modules/punk-0.2.7.tm rename to src/vfs/_vfscommon.vfs/modules/punk-0.2.8.tm index aac58c5f..ce6dd983 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk-0.2.7.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk-0.2.8.tm @@ -9002,7 +9002,14 @@ namespace eval punk { runtime-artifact metadata target field. The raw Tcl platform-package identifiers are shown for comparison (punk::platform::normalize folds their version-dependent - aliases - amd64/aarch64/macos - into the canonical names)." + aliases - amd64/aarch64/macos - into the canonical names, and + folds the msys2 family - mingw64/mingw32/ucrt64/clang* - onto + the one 'msys' tag, since msys2's uname renders that token from + MSYSTEM rather than from the binary). + These are HOST names. What a name implies for build OUTPUT + (.exe suffixing, process tooling, which bin/runtime tier holds + a runtime) is a TARGET question src/make.tcl derives from the + name - a cygwin-family host targets win32-x86_64 by default." @values -min 0 -max 0 } proc platforms {context args} { @@ -9505,7 +9512,7 @@ punkcheck::cli set_alias punkcheck package provide punk [namespace eval punk { #FUNCTL variable version - set version 0.2.7 + set version 0.2.8 }] diff --git a/src/vfs/_vfscommon.vfs/modules/punk/mix/templates-0.2.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/mix/templates-0.2.0.tm index 65c4a3150e0fd942f51e16187f4bec77ddc17314..5b144e73b58d1b75d6399db6efd9d2293fedfc79 100644 GIT binary patch delta 88774 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 delta 63624 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 XkRdW*(7aiu{u@)kn@mG*w}Afx#L3t- diff --git a/src/bootsupport/modules/punk/platform-0.1.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/platform-0.2.0.tm similarity index 73% rename from src/bootsupport/modules/punk/platform-0.1.0.tm rename to src/vfs/_vfscommon.vfs/modules/punk/platform-0.2.0.tm index 41b41736..b74ad186 100644 --- a/src/bootsupport/modules/punk/platform-0.1.0.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/platform-0.2.0.tm @@ -7,7 +7,7 @@ # (C) 2026 # # @@ Meta Begin -# Application punk::platform 0.1.0 +# Application punk::platform 0.2.0 # Meta platform tcl # Meta license BSD # @@ Meta End @@ -40,6 +40,29 @@ namespace eval punk::platform { # the darwin 'arm64' machine name to 'arm'; Apple silicon is 64-bit only, # so the arm token on macosx always means arm64. Elsewhere 'arm' is a # genuine 32-bit token and is preserved.) + # - os mingw64/mingw32/ucrt64/clang64/clangarm64 -> msys (G-122; see the + # MSYSTEM-variance note below) + # + #MSYSTEM VARIANCE (cygwin-family hosts on windows). An msys2-runtime tclsh + #reports tcl_platform(platform) 'unix' with an os string msys2's uname + #renders from the MSYSTEM environment variable, so ONE binary canonizes + #differently per launching shell - verified 2026-07-26 with msys2's + #/usr/bin/tclsh8.6: MSYSTEM=MSYS -> MSYS_NT-10.0-26200 (msys-x86_64), and + #MSYSTEM=MINGW64, =UCRT64 or unset (msys2 defaults it) -> MINGW64_NT-10.0-26200 + #(mingw64-x86_64). The MSYSTEM token names the shell that launched the + #process, not the binary's ABI: a genuinely MINGW-built tclsh is a native + #windows binary reporting os 'Windows NT' and canonizes as win32-*. So every + #msys2-runtime token folds to the one stable tag 'msys'. Real cygwin keeps + #its own tag 'cygwin' - a separate runtime dll and mount world, so a store + #tier serving one does not serve the other. Windows build numbers never + #reach a tag (platform::generic cuts the os at its first _ or -; normalize_os + #re-folds a raw uname-style string anyway). + #These are HOST canon names. What a tag implies for BUILD OUTPUT - .exe + #suffixing, which process tooling manages a running artifact, which + #bin/runtime store tier holds a runtime - is a TARGET question that + #src/make.tcl derives from the tag (G-122 host/target split): cygwin-family + #hosts default their build target to win32-x86_64, since the kits they bake + #are native windows binaries. #The special name 'macosx' (no cpu suffix) is the RUNTIME-tier convention for #universal (multi-arch) macOS binaries - punkbin and bin/runtime keep one #universal folder; the per-arch macosx-x86_64/macosx-arm64 names serve the @@ -83,7 +106,8 @@ namespace eval punk::platform { macosx-arm64 {status supported tiers {lib} buildsuite none notes "Apple silicon; would follow a macosx runtime arc"} freebsd-x86_64 {status supported tiers {runtime lib} buildsuite candidate notes ""} freebsd-arm64 {status supported tiers {runtime lib} buildsuite candidate notes "no artifacts yet"} - msys-x86_64 {status dormant tiers {lib} buildsuite none notes "msys/cygwin-built tclsh runtimes; utility under review"} + msys-x86_64 {status dormant tiers {runtime lib} buildsuite none notes "msys2-runtime tclsh on windows (posix personality, .exe files); host canon for every MSYSTEM shell - see normalize"} + cygwin-x86_64 {status recognized tiers {runtime lib} buildsuite none notes "cygwin-runtime tclsh on windows; separate dll/mount world from msys2, so a distinct tier"} openbsd-x86_64 {status recognized tiers {runtime} buildsuite none notes "hosted third-party runtimes possible"} netbsd-x86_64 {status recognized tiers {runtime} buildsuite none notes "hosted third-party runtimes possible"} dragonflybsd-x86_64 {status recognized tiers {runtime} buildsuite none notes "hosted third-party runtimes possible"} @@ -124,6 +148,45 @@ namespace eval punk::platform { return $platforms } + namespace eval argdoc { + variable PUNKARGS + lappend PUNKARGS [list { + @id -id ::punk::platform::normalize_os + @cmd -name punk::platform::normalize_os\ + -summary\ + "Fold an os token to its canonical punkshell spelling."\ + -help\ + "Fold a single os token (the half of an - platform + identifier) to its canonical punkshell spelling: + macos -> macosx + mingw32|mingw64|ucrt64|clang* -> msys + MSYS_NT- -> msys + CYGWIN_NT- -> cygwin + The msys2 family folds to one tag because msys2's uname renders + the os string from the MSYSTEM environment variable: the same + binary reports MSYS_NT or MINGW64_NT depending on which shell + launched it, while a genuinely MINGW-built (native windows) + tclsh reports 'Windows NT' and canonizes as win32. Raw + uname-style strings carrying a windows build number fold too - + a build number never belongs in a platform tag. + Unrecognized tokens pass through unchanged." + @values -min 1 -max 1 + os -type string -help\ + "os token, e.g the half of platform::generic's result." + }] + } + proc normalize_os {os} { + switch -glob -- [string tolower $os] { + macos {return macosx} + msys - msys_nt-* {return msys} + mingw32 - mingw64 - mingw_nt-* - mingw32_nt-* - mingw64_nt-* {return msys} + ucrt64 - ucrt64_nt-* {return msys} + clang32 - clang64 - clangarm64 - clang32_nt-* - clang64_nt-* - clangarm64_nt-* {return msys} + cygwin_nt-* {return cygwin} + } + return $os + } + namespace eval argdoc { variable PUNKARGS lappend PUNKARGS [list { @@ -137,6 +200,9 @@ namespace eval punk::platform { platform-dir name: cpu amd64 -> x86_64, aarch64 -> arm64 os macos -> macosx (Tcl platform 1.1.x renamed modern macOS) + os mingw64/mingw32/ucrt64/clang* -> msys (one stable tag for + the msys2 runtime family - the reported name follows MSYSTEM, + not the binary; see punk::platform::normalize_os) macosx cpu arm -> arm64 (platform::generic's arm* glob folds the darwin arm64 machine name to arm; Apple silicon is 64-bit only - non-macosx 'arm' stays 32-bit arm) @@ -150,18 +216,13 @@ namespace eval punk::platform { proc normalize {platform} { set parts [split $platform -] if {[llength $parts] < 2} { - #single-token names (e.g the universal 'macosx') - fold macos only - if {$platform eq "macos"} { - return macosx - } - return $platform + #single-token names (e.g the universal 'macosx') - fold the os token only + return [normalize_os $platform] } #os may itself contain a dash in principle - treat last token as cpu set cpu [lindex $parts end] set os [join [lrange $parts 0 end-1] -] - switch -- $os { - macos {set os macosx} - } + set os [normalize_os $os] switch -- $cpu { amd64 {set cpu x86_64} aarch64 {set cpu arm64} @@ -232,6 +293,6 @@ namespace eval ::punk::args::register { ## Ready package provide punk::platform [namespace eval punk::platform { variable version - set version 0.1.0 + set version 0.2.0 }] return diff --git a/src/vfs/_vfscommon.vfs/modules/punk/zip-0.1.1.tm b/src/vfs/_vfscommon.vfs/modules/punk/zip-0.1.1.tm deleted file mode 100644 index 02415ccd..00000000 --- a/src/vfs/_vfscommon.vfs/modules/punk/zip-0.1.1.tm +++ /dev/null @@ -1,914 +0,0 @@ -# -*- tcl -*- -# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from -buildversion.txt -# module template: punkshell/src/decktemplates/vendor/punk/modules/template_module-0.0.3.tm -# -# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. -# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -# (C) 2024 JMN -# (C) 2009 Path Thoyts -# -# @@ Meta Begin -# Application punk::zip 0.1.1 -# Meta platform tcl -# Meta license MIT -# @@ Meta End - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -# doctools header -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -#*** !doctools -#[manpage_begin punkshell_module_punk::zip 0 0.1.1] -#[copyright "2024"] -#[titledesc {Module API}] [comment {-- Name section and table of contents description --}] -#[moddesc {-}] [comment {-- Description at end of page heading --}] -#[require punk::zip] -#[keywords module zip fileformat] -#[description] -#[para] - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - -#*** !doctools -#[section Overview] -#[para] overview of punk::zip -#[subsection Concepts] -#[para] - - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -## Requirements -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - -#*** !doctools -#[subsection dependencies] -#[para] packages used by punk::zip -#[list_begin itemized] - -package require Tcl 8.6- -package require punk::args -#*** !doctools -#[item] [package {Tcl 8.6}] -#[item] [package {punk::args}] - -#*** !doctools -#[list_end] - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - -#*** !doctools -#[section API] - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -# Base namespace -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -tcl::namespace::eval punk::zip { - tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase - #variable xyz - - #*** !doctools - #[subsection {Namespace punk::zip}] - #[para] Core API functions for punk::zip - #[list_begin definitions] - - proc Path_a_atorbelow_b {path_a path_b} { - return [expr {[StripPath $path_b $path_a] ne $path_a}] - } - proc Path_a_at_b {path_a path_b} { - return [expr {[StripPath $path_a $path_b] eq "." }] - } - - proc Path_strip_alreadynormalized_prefixdepth {path prefix} { - if {$prefix eq ""} { - return $path - } - set pathparts [file split $path] - set prefixparts [file split $prefix] - if {[llength $prefixparts] >= [llength $pathparts]} { - return "" - } - return [file join \ - {*}[lrange \ - $pathparts \ - [llength $prefixparts] \ - end]] - } - - #StripPath - borrowed from tcllib fileutil - # ::fileutil::stripPath -- - # - # If the specified path references/is a path in prefix (or prefix itself) it - # is made relative to prefix. Otherwise it is left unchanged. - # In the case of it being prefix itself the result is the string '.'. - # - # Arguments: - # prefix prefix to strip from the path. - # path path to modify - # - # Results: - # path The (possibly) modified path. - - if {[string equal $::tcl_platform(platform) windows]} { - # Windows. While paths are stored with letter-case preserved al - # comparisons have to be done case-insensitive. For reference see - # SF Tcllib Bug 2499641. - - proc StripPath {prefix path} { - # [file split] is used to generate a canonical form for both - # paths, for easy comparison, and also one which is easy to modify - # using list commands. - - set prefix [file split $prefix] - set npath [file split $path] - - if {[string equal -nocase $prefix $npath]} { - return "." - } - - if {[string match -nocase "${prefix} *" $npath]} { - set path [eval [linsert [lrange $npath [llength $prefix] end] 0 file join ]] - } - return $path - } - } else { - proc StripPath {prefix path} { - # [file split] is used to generate a canonical form for both - # paths, for easy comparison, and also one which is easy to modify - # using list commands. - - set prefix [file split $prefix] - set npath [file split $path] - - if {[string equal $prefix $npath]} { - return "." - } - - if {[string match "${prefix} *" $npath]} { - set path [eval [linsert [lrange $npath [llength $prefix] end] 0 file join ]] - } - return $path - } - } - - proc Timet_to_dos {time_t} { - #*** !doctools - #[call [fun Timet_to_dos] [arg time_t]] - #[para] convert a unix timestamp into a DOS timestamp for ZIP times. - #[example { - # DOS timestamps are 32 bits split into bit regions as follows: - # 24 16 8 0 - # +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - # |Y|Y|Y|Y|Y|Y|Y|m| |m|m|m|d|d|d|d|d| |h|h|h|h|h|m|m|m| |m|m|m|s|s|s|s|s| - # +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - #}] - set s [clock format $time_t -format {%Y %m %e %k %M %S}] - scan $s {%d %d %d %d %d %d} year month day hour min sec - expr {(($year-1980) << 25) | ($month << 21) | ($day << 16) - | ($hour << 11) | ($min << 5) | ($sec >> 1)} - } - punk::args::define { - @id -id ::punk::zip::walk - @cmd -name punk::zip::walk -help\ - "Walk the directory structure starting at base/<-subpath> - and return a list of the files and folders encountered. - Resulting paths are relative to base unless -resultrelative - is supplied. - Folder names will end with a trailing slash. - " - -resultrelative -optional 1 -help\ - "Resulting paths are relative to this value. - Defaults to the value of base. If empty string - is given to -resultrelative the paths returned - are effectively absolute paths." - -emptydirs -default 0 -type boolean -help\ - "Whether to include directory trees in the result which had no - matches for the given fileglobs. - Intermediate dirs are always returned if there is a match with - fileglobs further down even if -emptdirs is 0. - " - -excludes -default "" -help "list of glob expressions to match against files and exclude" - -subpath -default "" -help\ - "May contain glob chars for folder elements" - #If we don't include --, the call walk -- .. will return nothing as 'base' will receive the -- - -- -type none -optional 1 - @values -min 1 -max -1 - base - fileglobs -default {*} -multiple 1 - } - proc walk {args} { - #*** !doctools - #[call [fun walk] [arg ?options?] [arg base]] - #[para] Walk a directory tree rooted at base - #[para] the -excludes list can be a set of glob expressions to match against files and avoid - #[para] e.g - #[example { - # punk::zip::walk -exclude {CVS/* *~.#*} library - #}] - - #todo: -relative 0|1 flag? - set argd [punk::args::parse $args withid ::punk::zip::walk] - set base [dict get $argd values base] - set fileglobs [dict get $argd values fileglobs] - set subpath [dict get $argd opts -subpath] - set excludes [dict get $argd opts -excludes] - set emptydirs [dict get $argd opts -emptydirs] - - set received [dict get $argd received] - - set imatch [list] - foreach fg $fileglobs { - lappend imatch [file join $subpath $fg] - } - - if {![dict exists $received -resultrelative]} { - set relto $base - set prefix "" - } else { - set relto [file normalize [dict get $argd opts -resultrelative]] - if {$relto ne ""} { - if {![Path_a_atorbelow_b $base $relto]} { - error "punk::zip::walk base must be at or below -resultrelative value (backtracking not currently supported)" - } - set prefix [Path_strip_alreadynormalized_prefixdepth $base $relto] - } else { - set prefix $base - } - } - - set result {} - #set imatch [file join $subpath $match] - set files [glob -nocomplain -tails -types f -directory $base -- {*}$imatch] - foreach file $files { - set excluded 0 - foreach glob $excludes { - if {[string match $glob $file]} { - set excluded 1 - break - } - } - if {!$excluded} {lappend result [file join $prefix $file]} - } - foreach dir [glob -nocomplain -tails -types d -directory $base -- [file join $subpath *]] { - set submatches [walk -subpath $dir -emptydirs $emptydirs -excludes $excludes $base {*}$fileglobs] - set subdir_entries [list] - set thisdir_match [list] - set has_file 0 - foreach sd $submatches { - set fullpath [file join $prefix $sd] ;#file join destroys trailing slash - if {[string index $sd end] eq "/"} { - lappend subdir_entries $fullpath/ - } else { - set has_file 1 - lappend subdir_entries $fullpath - } - } - if {$emptydirs} { - set thisdir_match [list "[file join $prefix $dir]/"] - } else { - if {$has_file} { - set thisdir_match [list "[file join $prefix $dir]/"] - } else { - set subdir_entries [list] - } - } - #NOTE: trailing slash required for entries to be recognised as 'file type' = "directory" - #This is true for 2024 Tcl9 mounted zipfs at least. zip utilities such as 7zip seem(icon correct) to recognize dirs with or without trailing slash - #Although there are attributes on some systems to specify if entry is a directory - it appears trailing slash should always be used for folder names. - set result [list {*}$result {*}$thisdir_match {*}$subdir_entries] - } - return $result - } - - #if there is an external preamble - extract that. (if there is also an internal preamble - ignore and consider part of the archive-data) - #Otherwise extract an internal preamble. - #if neither -? - #review - reconsider auto-determination of internal vs external preamble - punk::args::define { - @id -id ::punk::zip::extract_preamble - @cmd -name punk::zip::extract_preamble -help\ - "Split a zipfs based executable or library into its constituent - binary and zip parts. - - Note that the binary preamble might be either 'within' the zip offsets, - or simply catenated prior to an unadjusted zip. - Some build processes may have 'adjusted' the zip offsets to make the zip cover the entire file - ('file based' offset) whilst the more modern approach is to simply concatenate the binary and the zip - ('archive based' offset). An archive-based offset is simpler and more reliably points to the proper - split location. It also allows 'zipfs info //zipfs:/app' to return the correct offset information. - - Either way, extract_preamble can usually separate them, but in the unusual case that there is both an - external preamble and a preamble within the zip, only the external preamble will be split, with the - internal one remaining in the zip. - - The inverse of this process would be to extract the .zip file created by this split to a folder, - e.g extracted_zip_folder (adjusting contents as required) and then to run: - zipfs mkimg newbinaryname.exe extracted_zip_folder \"\" - " - @values -min 2 -max 3 - infile -type file -optional 0 -help\ - "Name of existing tcl executable or shared lib with attached zipfs filesystem" - outfile_preamble -optional 0 -type file -help\ - "Name of output file for binary preamble to be extracted to. - If this file already exists, an error will be raised" - outfile_zip -default "" -type file -help\ - "Name of output file for zip data to be extracted to. - If this file already exists, an error will be raised" - } - proc extract_preamble {args} { - set argd [punk::args::parse $args withid ::punk::zip::extract_preamble] - lassign [dict values $argd] leaders opts values received - - set infile [dict get $values infile] - set outfile_preamble [dict get $values outfile_preamble] - set outfile_zip [dict get $values outfile_zip] - - set inzip [open $infile r] - fconfigure $inzip -encoding iso8859-1 -translation binary - if {[file exists $outfile_preamble]} { - error "outfile_preamble $outfile_preamble already exists - please remove first" - } - if {$outfile_zip ne ""} { - if {[file exists $outfile_zip] && [file size $outfile_zip]} { - error "outfile_zip $outfile_zip already exists - please remove first" - } - } - chan seek $inzip 0 end - set insize [tell $inzip] ;#faster (including seeks) than calling out to filesystem using file size - but should be equivalent - chan seek $inzip 0 start - #only scan last 64k - cover max signature size?? review - if {$insize < 65559} { - set tailsearch_start 0 - } else { - set tailsearch_start [expr {$insize - 65559}] - } - chan seek $inzip $tailsearch_start start - set scan [read $inzip] - #EOCD - End Of Central Directory record - set start_of_end [string last "\x50\x4b\x05\x06" $scan] - puts stdout "==>start_of_end: $start_of_end" - - if {$start_of_end == -1} { - #no zip eocdr - consider entire file to be the zip preamble - set baseoffset $insize - } else { - set filerelative_eocd_posn [expr {$start_of_end + $tailsearch_start}] - chan seek $inzip $filerelative_eocd_posn - set cdir_record_plus [read $inzip] ;#can have trailing data - binary scan $cdir_record_plus issssiis eocd(signature) eocd(disknbr) eocd(ctrldirdisk) \ - eocd(numondisk) eocd(totalnum) eocd(dirsize) eocd(diroffset) eocd(comment_len) - #rule out a false positive from within a nonzip (e.g plain exe) - #There exists for example a PK\5\6 in a plain tclsh, but it doesn't appear to be zip related. - #It doesn't seem to occur near the end - so perhaps not an issue - but we'll do some basic checks anyway - #we only support single disk - so we'll validate a bit more by requiring disknbr and ctrldirdisk to be zeros - #todo - just search for Pk\5\6\0\0\0\0 in the first place? //review - if {$eocd(disknbr) + $eocd(ctrldirdisk) != 0} { - #review - should keep searching? - #for now we assume not a zip - set baseoffset $insize - } else { - #use the central dir size to jump back tko start of central dir - #determine if diroffset is file or archive relative - - set filerelative_cdir_start [expr {$filerelative_eocd_posn - $eocd(dirsize)}] - puts stdout "---> [read $inzip 4]" - if {$filerelative_cdir_start > $eocd(diroffset)} { - #'external preamble' easy case - # - ie 'archive' offset - (and one of the reasons I prefer archive-offset - it makes finding the 'prefix' easier - #though we are assuming zip offsets are not corrupted - set baseoffset [expr {$filerelative_cdir_start - $eocd(diroffset)}] - } else { - #'internal preamble' hard case - # - either no preamble - or offsets have been adjusted to be file relative. - #we could scan from top (ugly) - and with binary prefixes we could get false positives in the data that look like PK\3\4 headers - #we could either work out the format for all possible executables that could be appended (across all platforms) and understand where they end? - #or we just look for the topmost PK\3\4 header pointed to by a CDR record - and assume the CDR is complete - - #step one - read all the CD records and find the highest pointed to local file record (which isn't necessarily the first - but should get us above most if not all of the zip data) - #we can't assume they're ordered in any particular way - so we in theory have to look at them all. - set baseoffset "unknown" - chan seek $inzip $filerelative_cdir_start start - #binary scan $cdir_record_plus issssiis eocd(signature) eocd(disknbr) eocd(ctrldirdisk) \ - # eocd(numondisk) eocd(totalnum) eocd(dirsize) eocd(diroffset) eocd(comment_len) - #load the whole central dir into cdir - - #todo! loop through all cdr file headers - find highest offset? - #tclZipfs.c just looks at first file header in Central Directory - #looking at all entries would be more robust - but we won't work harder than tclZipfs.c for now //REVIEW - - set cdirdata [read $inzip $eocd(dirsize)] - binary scan $cdirdata issssssiiisssssii cdir(signature) cdir(_vermadeby) cdir(_verneeded) cdir(gpbitflag) cdir(compmethod) cdir(lastmodifiedtime) cdir(lastmodifieddate)\ - cdir(uncompressedcrc32) cdir(compressedsize) cdir(uncompressedsize) cdir(filenamelength) cdir(extrafieldlength) cdir(filecommentlength) cdir(disknbr)\ - cdir(internalfileattributes) cdir(externalfileatributes) cdir(relativeoffset) - - #since we're in this branch - we assume cdir(relativeoffset) is from the start of the file - chan seek $inzip $cdir(relativeoffset) - #let's at least check that we landed on a local file header.. - set local_file_header_beginning [read $inzip 28]; #local_file_header without the file name and extra field - binary scan $local_file_header_beginning isssssiiiss lfh(signature) lfh(_verneeded) lfh(gpbitflag) lfh(compmethod) lfh(lastmodifiedtime) lfh(lastmodifieddate)\ - lfh(uncompressedcrc32) lfh(compressedsize) lfh(uncompressedsize) lfh(filenamelength) lfh(extrafieldlength) - #dec2hex 67324752 = 4034B50 = PK\3\4 - puts stdout "1st local file header sig: $lfh(signature)" - if {$lfh(signature) == 67324752} { - #looks like a local file header - #use our cdir(relativeoffset) as the start of the zip-data (//review - possible embedded password + end marker preceeding this) - set baseoffset $cdir(relativeoffset) - } - } - puts stdout "filerel_cdirstart: $filerelative_cdir_start recorded_offset: $eocd(diroffset)" - } - } - puts stdout "baseoffset: $baseoffset" - #expect CDFH PK\1\2 - #above the CD - we expect a bunch of PK\3\4 records - (possibly not all of them pointed to by the CDR) - #above that we expect: *possibly* a stored password with trailing marker - then the prefixed exe/script - - if {![string is integer -strict $baseoffset]} { - error "unable to determine zip baseoffset of file $infile" - } - - if {$baseoffset < $insize} { - set pout [open $outfile_preamble w] - fconfigure $pout -encoding iso8859-1 -translation binary - chan seek $inzip 0 start - chan copy $inzip $pout -size $baseoffset - close $pout - if {$outfile_zip ne ""} { - #todo - if it was internal preamble - need to adjust offsets to fix the split off zipfile - set zout [open $outfile_zip w] - fconfigure $zout -encoding iso8859-1 -translation binary - chan copy $inzip $zout - close $zout - } - close $inzip - } else { - #no valid (from our perspective) eocdr found - baseoffset has been set to insize - close $inzip - file copy $infile $outfile_preamble - if {$outfile_zip ne ""} { - #touch equiv? - set fd [open $outfile_zip w] - close $fd - } - } - } - - - - punk::args::define { - @id -id ::punk::zip::Addentry - @cmd -name punk::zip::Addentry\ - -summary\ - "Add zip-entry for file at 'path'"\ - -help\ - "Add a single file at 'path' to open channel 'zipchan' - return a central directory file record" - @opts - -comment -default "" -help "An optional comment specific to the added file" - @values -min 3 -max 4 - zipchan -help "open file descriptor with cursor at position appropriate for writing a local file header" - base -help "base path for entries" - path -type file -help "path of file to add" - zipdataoffset -default 0 -type integer -range {0 ""} -help "offset of start of zip-data - ie length of prefixing script/exe - Can be specified as zero even if a prefix exists - which would make offsets 'file relative' as opposed to 'archive relative'" - } - - # Addentry - was Mkzipfile -- - # - # FIX ME: should handle the current offset for non-seekable channels - # - proc Addentry {args} { - #*** !doctools - #[call [fun Addentry] [arg zipchan] [arg base] [arg path] [arg ?comment?]] - #[para] Add a single file to a zip archive - #[para] The zipchan channel should already be open and binary. - #[para] You can provide a -comment for the file. - #[para] The return value is the central directory record that will need to be used when finalizing the zip archive. - - set argd [punk::args::parse $args withid ::punk::zip::Addentry] - set zipchan [dict get $argd values zipchan] - set base [dict get $argd values base] - set path [dict get $argd values path] - set zipdataoffset [dict get $argd values zipdataoffset] - - set comment [dict get $argd opts -comment] - - set fullpath [file join $base $path] - set mtime [Timet_to_dos [file mtime $fullpath]] - set utfpath [encoding convertto utf-8 $path] - set utfcomment [encoding convertto utf-8 $comment] - set flags [expr {(1<<11)}] ;# utf-8 comment and path - set method 0 ;# store 0, deflate 8 - set attr 0 ;# text or binary (default binary) - set version 20 ;# minumum version req'd to extract - set extra "" - set crc 0 - set size 0 - set csize 0 - set data "" - set seekable [expr {[tell $zipchan] != -1}] - if {[file isdirectory $fullpath]} { - set attrex 0x41ff0010 ;# 0o040777 (drwxrwxrwx) - #set attrex 0x40000010 - } elseif {[file executable $fullpath]} { - set attrex 0x81ff0080 ;# 0o100777 (-rwxrwxrwx) - } else { - set attrex 0x81b60020 ;# 0o100666 (-rw-rw-rw-) - if {[file extension $fullpath] in {".tcl" ".txt" ".c"}} { - set attr 1 ;# text - } - } - - if {[file isfile $fullpath]} { - set size [file size $fullpath] - if {!$seekable} {set flags [expr {$flags | (1 << 3)}]} - } - - - set channeloffset [tell $zipchan] ;#position in the channel - this may include prefixing exe/zip - set local [binary format a4sssiiiiss PK\03\04 \ - $version $flags $method $mtime $crc $csize $size \ - [string length $utfpath] [string length $extra]] - append local $utfpath $extra - puts -nonewline $zipchan $local - - if {[file isfile $fullpath]} { - # If the file is under 2MB then zip in one chunk, otherwize we use - # streaming to avoid requiring excess memory. This helps to prevent - # storing re-compressed data that may be larger than the source when - # handling PNG or JPEG or nested ZIP files. - if {$size < 0x00200000} { - set fin [open $fullpath rb] - set data [read $fin] - set crc [zlib crc32 $data] - set cdata [zlib deflate $data] - if {[string length $cdata] < $size} { - set method 8 - set data $cdata - } - close $fin - set csize [string length $data] - puts -nonewline $zipchan $data - } else { - set method 8 - set fin [open $fullpath rb] - set zlib [zlib stream deflate] - while {![eof $fin]} { - set data [read $fin 4096] - set crc [zlib crc32 $data $crc] - $zlib put $data - if {[string length [set zdata [$zlib get]]]} { - incr csize [string length $zdata] - puts -nonewline $zipchan $zdata - } - } - close $fin - $zlib finalize - set zdata [$zlib get] - incr csize [string length $zdata] - puts -nonewline $zipchan $zdata - $zlib close - } - - if {$seekable} { - # update the header if the output is seekable - set local [binary format a4sssiiii PK\03\04 \ - $version $flags $method $mtime $crc $csize $size] - set current [tell $zipchan] - seek $zipchan $channeloffset - puts -nonewline $zipchan $local - seek $zipchan $current - } else { - # Write a data descriptor record - set ddesc [binary format a4iii PK\7\8 $crc $csize $size] - puts -nonewline $zipchan $ddesc - } - } - - #PK\x01\x02 Cdentral directory file header - #set v1 0x0317 ;#upper byte 03 -> UNIX lower byte 23 -> 2.3 - set v1 0x0017 ;#upper byte 00 -> MS_DOS and OS/2 (FAT/VFAT/FAT32 file systems) - - set hdr [binary format a4ssssiiiisssssii PK\01\02 $v1 \ - $version $flags $method $mtime $crc $csize $size \ - [string length $utfpath] [string length $extra]\ - [string length $utfcomment] 0 $attr $attrex [expr {$channeloffset - $zipdataoffset}]] ;#zipdataoffset may be zero - either because it's a pure zip, or file-based offsets desired. - append hdr $utfpath $extra $utfcomment - return $hdr - } - - #### REVIEW!!! - #JMN - review - this looks to be offset relative to start of file - (same as 2024 Tcl 'mkzip mkimg') - # we want to enable (optionally) offsets relative to start of archive for exe/script-prefixed zips.on windows (editability with 7z,peazip) - #### - - - punk::args::define { - @id -id ::punk::zip::mkzip - @cmd -name punk::zip::mkzip\ - -summary\ - "Create a zip archive in 'filename'."\ - -help\ - "Create a zip archive in 'filename'" - @opts - -offsettype -default "archive" -choices {archive file}\ - -help\ - "zip offsets stored relative to start of entire file or relative to start of zip-archive - Only relevant if the created file has a script/runtime prefix." - -return -default "pretty" -choices {pretty list none}\ - -help\ - "mkzip can return a list of the files and folders added to the archive - the option -return pretty is the default and uses the punk::lib pdict/plist system - to return a formatted list for the terminal - " - -zipkit -default 0 -type none\ - -help\ - "whether to add mounting script - mutually exclusive with -runtime option - currently vfs::zip based - todo - autodetect zipfs/vfs with pref for zipfs" - -runtime -default ""\ - -help\ - "specify a prefix file - e.g punk::zip::mkzip -runtime unzipsfx.exe -directory subdir -base subdir output.zip - will create a self-extracting zip archive from the subdir/ folder. - Expects runtime with no existing vfs attached (review)" - -comment -default ""\ - -help "An optional comment for the archive" - -directory -default ""\ - -help "Scan for contents within this folder or current directory if not provided." - -base -default ""\ - -help\ - "The new zip archive will be rooted in this directory if provided - it must be a parent of -directory or the same path as -directory" - -exclude -default {CVS/* */CVS/* *~ ".#*" "*/.#*"} - -- -type none -help\ - "End of options marker" - - @values -min 1 -max -1 - filename -type file -default ""\ - -help "name of zipfile to create" - globs -default {*} -multiple 1\ - -help\ - "list of glob patterns to match. - Only directories with matching files will be included in the archive." - } - - # zip::mkzip -- - # - # eg: zip my.zip -directory Subdir -runtime unzipsfx.exe *.txt - # - proc mkzip {args} { - #todo - doctools - [arg ?globs...?] syntax? - - #*** !doctools - #[call [fun mkzip]\ - # [opt "[option -offsettype] [arg offsettype]"]\ - # [opt "[option -return] [arg returntype]"]\ - # [opt "[option -zipkit] [arg 0|1]"]\ - # [opt "[option -runtime] [arg preamble_filename]"]\ - # [opt "[option -comment] [arg zipfilecomment]"]\ - # [opt "[option -directory] [arg dir_to_zip]"]\ - # [opt "[option -base] [arg archive_root]"]\ - # [opt "[option -exclude] [arg globlist]"]\ - # [arg zipfilename]\ - # [arg ?glob...?]] - #[para] Create a zip archive in 'zipfilename' - #[para] If a file already exists, an error will be raised. - #[para] Call 'punk::zip::mkzip' with no arguments for usage display. - - set argd [punk::args::parse $args withid ::punk::zip::mkzip] - set filename [dict get $argd values filename] - if {$filename eq ""} { - error "mkzip filename cannot be empty string" - } - if {[regexp {[?*]} $filename]} { - #catch a likely error where filename is omitted and first glob pattern is misinterpreted as zipfile name - error "mkzip filename should not contain glob characters ? *" - } - if {[file exists $filename]} { - error "mkzip filename:$filename already exists" - } - dict for {k v} [dict get $argd opts] { - switch -- $k { - -comment { - dict set argd opts $k [encoding convertto utf-8 $v] - } - -directory - -base { - dict set argd opts $k [file normalize $v] - } - } - } - - array set opts [dict get $argd opts] - - - if {$opts(-directory) ne ""} { - if {$opts(-base) ne ""} { - #-base and -directory have been normalized already - if {![Path_a_atorbelow_b $opts(-directory) $opts(-base)]} { - error "punk::zip::mkzip -base $opts(-base) must be above or the same as -directory $opts(-directory)" - } - set base $opts(-base) - set relpath [Path_strip_alreadynormalized_prefixdepth $opts(-directory) $opts(-base)] - } else { - set base $opts(-directory) - set relpath "" - } - #will pick up intermediary folders as paths (ending with trailing slash) - set paths [walk -exclude $opts(-exclude) -subpath $relpath -- $base {*}[dict get $argd values globs]] - - set norm_filename [file normalize $filename] - set norm_dir [file normalize $opts(-directory)] ;#we only care if filename below -directory (which is where we start scanning) - if {[Path_a_atorbelow_b $norm_filename $norm_dir]} { - #check that we aren't adding the zipfile to itself - #REVIEW - now that we open zipfile after scanning - this isn't really a concern! - #keep for now in case we can add an -update or a -force facility (or in case we modify to add to zip as we scan for members?) - #In the case of -force - we may want to delay replacement of original until scan is done? - - #try to avoid looping on all paths and performing (somewhat) expensive file normalizations on each - #1st step is to check the patterns and see if our zipfile is already excluded - in which case we need not check the paths - set self_globs_match 0 - foreach g [dict get $argd values globs] { - if {[string match $g [file tail $filename]]} { - set self_globs_match 1 - break - } - } - if {$self_globs_match} { - #still dangerous - set self_excluded 0 - foreach e $opts(-exclude) { - if {[string match $e [file tail $filename]]} { - set self_excluded 1 - break - } - } - if {!$self_excluded} { - #still dangerous - likely to be in resultset - check each path - #puts stderr "zip file $filename is below directory $opts(-directory)" - set self_is_matched 0 - set i 0 - foreach p $paths { - set norm_p [file normalize [file join $opts(-directory) $p]] - if {[Path_a_at_b $norm_filename $norm_p]} { - set self_is_matched 1 - break - } - incr i - } - if {$self_is_matched} { - puts stderr "WARNING - zipfile being created '$filename' was matched. Excluding this file. Relocate the zip, or use -exclude patterns to avoid this message" - set paths [lremove $paths $i] - } - } - } - } - } else { - #NOTE that we don't add intermediate folders when creating an archive without using the -directory flag! - #ie - only the exact *files* matching the glob are stored. - set paths [list] - set dir [pwd] - if {$opts(-base) ne ""} { - if {![Path_a_atorbelow_b $dir $opts(-base)]} { - error "punk::zip::mkzip -base $opts(-base) must be above current directory" - } - set relpath [Path_strip_alreadynormalized_prefixdepth [file normalize $dir] [file normalize $opts(-base)]] - } else { - set relpath "" - } - set base $opts(-base) - - set matches [glob -nocomplain -type f -- {*}[dict get $argd values globs]] - foreach m $matches { - if {$m eq $filename} { - #puts stderr "--> excluding $filename" - continue - } - set isok 1 - foreach e [concat $opts(-exclude) $filename] { - if {[string match $e $m]} { - set isok 0 - break - } - } - if {$isok} { - lappend paths [file join $relpath $m] - } - } - } - - if {![llength $paths]} { - return "" - } - - set zf [open $filename wb] - if {$opts(-runtime) ne ""} { - #todo - strip any existing vfs - option to merge contents.. only if zip attached? - set rt [open $opts(-runtime) rb] - fcopy $rt $zf - close $rt - } elseif {$opts(-zipkit)} { - #TODO - update to zipfs ? - #see modpod - set zkd "#!/usr/bin/env tclkit\n\# This is a zip-based Tcl Module\n" - append zkd "package require vfs::zip\n" - append zkd "vfs::zip::Mount \[info script\] \[info script\]\n" - append zkd "if {\[file exists \[file join \[info script\] main.tcl\]\]} {\n" - append zkd " source \[file join \[info script\] main.tcl\]\n" - append zkd "}\n" - append zkd \x1A - puts -nonewline $zf $zkd - } - - #todo - subtract this from the endrec offset - if {$opts(-offsettype) eq "archive"} { - set dataStartOffset [tell $zf] ;#the overall file offset of the start of archive-data //JMN 2024 - } else { - set dataStartOffset 0 ;#offsets relative to file - the (old) zipfs mkzip way :/ - } - - set count 0 - set cd "" - - set members [list] - foreach path $paths { - #puts $path - lappend members $path - append cd [Addentry $zf $base $path $dataStartOffset] ;#path already includes relpath - incr count - } - set cdoffset [tell $zf] - set endrec [binary format a4ssssiis PK\05\06 0 0 \ - $count $count [string length $cd] [expr {$cdoffset - $dataStartOffset}]\ - [string length $opts(-comment)]] - append endrec $opts(-comment) - puts -nonewline $zf $cd - puts -nonewline $zf $endrec - close $zf - - set result "" - switch -exact -- $opts(-return) { - list { - set result $members - } - pretty { - if {[info commands showlist] ne ""} { - set result [plist -channel none members] - } else { - set result $members - } - } - none { - set result "" - } - } - return $result - } - - - #*** !doctools - #[list_end] [comment {--- end definitions namespace punk::zip ---}] -} -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -# Secondary API namespace -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -tcl::namespace::eval punk::zip::lib { - tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase - tcl::namespace::path [tcl::namespace::parent] - #*** !doctools - #[subsection {Namespace punk::zip::lib}] - #[para] Secondary functions that are part of the API - #[list_begin definitions] - - #proc utility1 {p1 args} { - # #*** !doctools - # #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]] - # #[para]Description of utility1 - # return 1 - #} - - - - #*** !doctools - #[list_end] [comment {--- end definitions namespace punk::zip::lib ---}] -} -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ - - - -# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -## Ready -package provide punk::zip [tcl::namespace::eval punk::zip { - variable pkg punk::zip - variable version - set version 0.1.1 -}] -return - -#*** !doctools -#[manpage_end] - diff --git a/src/vfs/_vfscommon.vfs/modules/punk/zip-0.2.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/zip-0.2.0.tm new file mode 100644 index 00000000..eb1651da --- /dev/null +++ b/src/vfs/_vfscommon.vfs/modules/punk/zip-0.2.0.tm @@ -0,0 +1,1698 @@ +# -*- tcl -*- +# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from -buildversion.txt +# module template: punkshell/src/decktemplates/vendor/punk/modules/template_module-0.0.3.tm +# +# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. +# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# (C) 2024 JMN +# (C) 2009 Path Thoyts +# +# @@ Meta Begin +# Application punk::zip 0.2.0 +# Meta platform tcl +# Meta license MIT +# @@ Meta End + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# doctools header +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +#*** !doctools +#[manpage_begin punkshell_module_punk::zip 0 0.2.0] +#[copyright "2024"] +#[titledesc {Module API}] [comment {-- Name section and table of contents description --}] +#[moddesc {-}] [comment {-- Description at end of page heading --}] +#[require punk::zip] +#[keywords module zip fileformat] +#[description] +#[para] - + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + +#*** !doctools +#[section Overview] +#[para] overview of punk::zip +#[subsection Concepts] +#[para] - + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +## Requirements +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + +#*** !doctools +#[subsection dependencies] +#[para] packages used by punk::zip +#[list_begin itemized] + +package require Tcl 8.6- +package require punk::args +#*** !doctools +#[item] [package {Tcl 8.6}] +#[item] [package {punk::args}] + +#*** !doctools +#[list_end] + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + +#*** !doctools +#[section API] + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# Base namespace +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +tcl::namespace::eval punk::zip { + tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase + #variable xyz + + #*** !doctools + #[subsection {Namespace punk::zip}] + #[para] Core API functions for punk::zip + #[list_begin definitions] + + proc Path_a_atorbelow_b {path_a path_b} { + return [expr {[StripPath $path_b $path_a] ne $path_a}] + } + proc Path_a_at_b {path_a path_b} { + return [expr {[StripPath $path_a $path_b] eq "." }] + } + + proc Path_strip_alreadynormalized_prefixdepth {path prefix} { + if {$prefix eq ""} { + return $path + } + set pathparts [file split $path] + set prefixparts [file split $prefix] + if {[llength $prefixparts] >= [llength $pathparts]} { + return "" + } + return [file join \ + {*}[lrange \ + $pathparts \ + [llength $prefixparts] \ + end]] + } + + #StripPath - borrowed from tcllib fileutil + # ::fileutil::stripPath -- + # + # If the specified path references/is a path in prefix (or prefix itself) it + # is made relative to prefix. Otherwise it is left unchanged. + # In the case of it being prefix itself the result is the string '.'. + # + # Arguments: + # prefix prefix to strip from the path. + # path path to modify + # + # Results: + # path The (possibly) modified path. + + if {[string equal $::tcl_platform(platform) windows]} { + # Windows. While paths are stored with letter-case preserved al + # comparisons have to be done case-insensitive. For reference see + # SF Tcllib Bug 2499641. + + proc StripPath {prefix path} { + # [file split] is used to generate a canonical form for both + # paths, for easy comparison, and also one which is easy to modify + # using list commands. + + set prefix [file split $prefix] + set npath [file split $path] + + if {[string equal -nocase $prefix $npath]} { + return "." + } + + if {[string match -nocase "${prefix} *" $npath]} { + set path [eval [linsert [lrange $npath [llength $prefix] end] 0 file join ]] + } + return $path + } + } else { + proc StripPath {prefix path} { + # [file split] is used to generate a canonical form for both + # paths, for easy comparison, and also one which is easy to modify + # using list commands. + + set prefix [file split $prefix] + set npath [file split $path] + + if {[string equal $prefix $npath]} { + return "." + } + + if {[string match "${prefix} *" $npath]} { + set path [eval [linsert [lrange $npath [llength $prefix] end] 0 file join ]] + } + return $path + } + } + + proc Timet_to_dos {time_t} { + #*** !doctools + #[call [fun Timet_to_dos] [arg time_t]] + #[para] convert a unix timestamp into a DOS timestamp for ZIP times. + #[example { + # DOS timestamps are 32 bits split into bit regions as follows: + # 24 16 8 0 + # +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + # |Y|Y|Y|Y|Y|Y|Y|m| |m|m|m|d|d|d|d|d| |h|h|h|h|h|m|m|m| |m|m|m|s|s|s|s|s| + # +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + #}] + set s [clock format $time_t -format {%Y %m %e %k %M %S}] + scan $s {%d %d %d %d %d %d} year month day hour min sec + expr {(($year-1980) << 25) | ($month << 21) | ($day << 16) + | ($hour << 11) | ($min << 5) | ($sec >> 1)} + } + + #Inverse of Timet_to_dos. DOS timestamps carry no timezone, and Timet_to_dos + #formats in local time - so this scans in local time to keep the pair symmetric. + #Entries stored with an unrepresentable date (zero, or corrupt) yield 0. + proc Dos_to_timet {dosdatetime} { + set dosdate [expr {($dosdatetime >> 16) & 0xFFFF}] + set dostime [expr {$dosdatetime & 0xFFFF}] + set year [expr {(($dosdate >> 9) & 0x7F) + 1980}] + set month [expr {($dosdate >> 5) & 0x0F}] + set day [expr {$dosdate & 0x1F}] + set hour [expr {($dostime >> 11) & 0x1F}] + set minute [expr {($dostime >> 5) & 0x3F}] + set second [expr {($dostime & 0x1F) << 1}] + if {$month < 1 || $month > 12 || $day < 1 || $day > 31 || $hour > 23 || $minute > 59 || $second > 59} { + return 0 + } + set stamp [format {%04d %02d %02d %02d %02d %02d} $year $month $day $hour $minute $second] + if {[catch {clock scan $stamp -format {%Y %m %d %H %M %S}} timet]} { + return 0 + } + return $timet + } + + #Compression method ids as they appear in a zip central directory. + #punk::zip READS 0 (store) and 8 (deflate); the rest are named so an + #unsupported archive is refused by name rather than by number. + variable methodnames + set methodnames [dict create {*}{ + 0 store + 1 shrink + 2 reduce1 + 3 reduce2 + 4 reduce3 + 5 reduce4 + 6 implode + 8 deflate + 9 deflate64 + 10 pkware-implode + 12 bzip2 + 14 lzma + 16 cmpsc + 18 terse + 19 lz77 + 20 zstd-deprecated + 93 zstd + 94 mp3 + 95 xz + 96 jpeg + 97 wavpack + 98 ppmd + 99 aes + }] + + proc Method_name {method} { + variable methodnames + if {[dict exists $methodnames $method]} { + return [dict get $methodnames $method] + } + return method-$method + } + punk::args::define { + @id -id ::punk::zip::walk + @cmd -name punk::zip::walk -help\ + "Walk the directory structure starting at base/<-subpath> + and return a list of the files and folders encountered. + Resulting paths are relative to base unless -resultrelative + is supplied. + Folder names will end with a trailing slash. + " + -resultrelative -optional 1 -help\ + "Resulting paths are relative to this value. + Defaults to the value of base. If empty string + is given to -resultrelative the paths returned + are effectively absolute paths." + -emptydirs -default 0 -type boolean -help\ + "Whether to include directory trees in the result which had no + matches for the given fileglobs. + Intermediate dirs are always returned if there is a match with + fileglobs further down even if -emptdirs is 0. + " + -excludes -default "" -help "list of glob expressions to match against files and exclude" + -subpath -default "" -help\ + "May contain glob chars for folder elements" + #If we don't include --, the call walk -- .. will return nothing as 'base' will receive the -- + -- -type none -optional 1 + @values -min 1 -max -1 + base + fileglobs -default {*} -multiple 1 + } + proc walk {args} { + #*** !doctools + #[call [fun walk] [arg ?options?] [arg base]] + #[para] Walk a directory tree rooted at base + #[para] the -excludes list can be a set of glob expressions to match against files and avoid + #[para] e.g + #[example { + # punk::zip::walk -exclude {CVS/* *~.#*} library + #}] + + #todo: -relative 0|1 flag? + set argd [punk::args::parse $args withid ::punk::zip::walk] + set base [dict get $argd values base] + set fileglobs [dict get $argd values fileglobs] + set subpath [dict get $argd opts -subpath] + set excludes [dict get $argd opts -excludes] + set emptydirs [dict get $argd opts -emptydirs] + + set received [dict get $argd received] + + set imatch [list] + foreach fg $fileglobs { + lappend imatch [file join $subpath $fg] + } + + if {![dict exists $received -resultrelative]} { + set relto $base + set prefix "" + } else { + set relto [file normalize [dict get $argd opts -resultrelative]] + if {$relto ne ""} { + if {![Path_a_atorbelow_b $base $relto]} { + error "punk::zip::walk base must be at or below -resultrelative value (backtracking not currently supported)" + } + set prefix [Path_strip_alreadynormalized_prefixdepth $base $relto] + } else { + set prefix $base + } + } + + set result {} + #set imatch [file join $subpath $match] + set files [glob -nocomplain -tails -types f -directory $base -- {*}$imatch] + foreach file $files { + set excluded 0 + foreach glob $excludes { + if {[string match $glob $file]} { + set excluded 1 + break + } + } + if {!$excluded} {lappend result [file join $prefix $file]} + } + foreach dir [glob -nocomplain -tails -types d -directory $base -- [file join $subpath *]] { + set submatches [walk -subpath $dir -emptydirs $emptydirs -excludes $excludes $base {*}$fileglobs] + set subdir_entries [list] + set thisdir_match [list] + set has_file 0 + foreach sd $submatches { + set fullpath [file join $prefix $sd] ;#file join destroys trailing slash + if {[string index $sd end] eq "/"} { + lappend subdir_entries $fullpath/ + } else { + set has_file 1 + lappend subdir_entries $fullpath + } + } + if {$emptydirs} { + set thisdir_match [list "[file join $prefix $dir]/"] + } else { + if {$has_file} { + set thisdir_match [list "[file join $prefix $dir]/"] + } else { + set subdir_entries [list] + } + } + #NOTE: trailing slash required for entries to be recognised as 'file type' = "directory" + #This is true for 2024 Tcl9 mounted zipfs at least. zip utilities such as 7zip seem(icon correct) to recognize dirs with or without trailing slash + #Although there are attributes on some systems to specify if entry is a directory - it appears trailing slash should always be used for folder names. + set result [list {*}$result {*}$thisdir_match {*}$subdir_entries] + } + return $result + } + + # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + # Archive structure - the shared reader machinery. + # + # Every reading surface (extract_preamble, archive_info, members, unzip) goes + # through Archive_read, so a plain zip and a zip attached to an executable are + # one code path, and the archive-relative vs file-relative offset convention is + # decided in exactly one place. + # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + + #Locate and validate the End Of Central Directory record. + #Scans candidate PK\5\6 signatures from the end of the file backwards - a plain + #executable can contain that byte sequence in its data, so a candidate is only + #accepted if it describes a single-disk directory that really begins with PK\1\2. + #The first pass also requires the record to end exactly at EOF (comment length + #consistent); a second pass drops that so archives with trailing junk still read. + proc Eocd_scan {chan filesize} { + set info [dict create {*}{ + status nozip + reason {no end-of-central-directory record found} + eocdoffset -1 + cdiroffset -1 + cdirsize 0 + count 0 + diroffset 0 + offsetbase 0 + comment {} + }] + dict set info filesize $filesize + if {$filesize < 22} { + return $info + } + set tailstart [expr {$filesize < 65559 ? 0 : $filesize - 65559}] + chan seek $chan $tailstart start + set tail [read $chan] + set candidates [list] + set idx [string length $tail] + while {1} { + set p [string last "\x50\x4b\x05\x06" $tail [expr {$idx - 1}]] + if {$p < 0} { + break + } + lappend candidates [expr {$p + $tailstart}] + set idx $p + } + foreach strict {1 0} { + foreach eocdoffset $candidates { + set try [Eocd_try $chan $filesize $eocdoffset $strict] + if {[dict get $try status] ne "reject"} { + return $try + } + } + } + return $info + } + + proc Eocd_try {chan filesize eocdoffset strict} { + if {$eocdoffset + 22 > $filesize} { + return [dict create status reject] + } + chan seek $chan $eocdoffset start + set rec [read $chan 22] + binary scan $rec issssiis sig disknbr cdirdisk numondisk totalnum cdirsize diroffset commentlen + set disknbr [expr {$disknbr & 0xFFFF}] + set cdirdisk [expr {$cdirdisk & 0xFFFF}] + set numondisk [expr {$numondisk & 0xFFFF}] + set totalnum [expr {$totalnum & 0xFFFF}] + set commentlen [expr {$commentlen & 0xFFFF}] + set cdirsize [expr {$cdirsize & 0xFFFFFFFF}] + set diroffset [expr {$diroffset & 0xFFFFFFFF}] + #single-disk archives only - anything else is a spanned archive or a false positive + if {$disknbr != 0 || $cdirdisk != 0 || $numondisk != $totalnum} { + return [dict create status reject] + } + if {$strict && $eocdoffset + 22 + $commentlen != $filesize} { + return [dict create status reject] + } + set info [dict create {*}{ + status ok + reason {} + } filesize $filesize {*}{ + } eocdoffset $eocdoffset {*}{ + } cdirsize $cdirsize {*}{ + } count $totalnum {*}{ + } diroffset $diroffset {*}{ + }] + #A saturated field means the real value lives in a zip64 record we do not read. + if {$totalnum == 0xFFFF || $cdirsize == 0xFFFFFFFF || $diroffset == 0xFFFFFFFF} { + dict set info status unsupported + dict set info reason "zip64 archive - punk::zip reads single-disk zip32 archives only" + dict set info cdiroffset -1 + dict set info offsetbase 0 + dict set info comment "" + return $info + } + set cdiroffset [expr {$eocdoffset - $cdirsize}] + if {$cdiroffset < 0} { + return [dict create status reject] + } + if {$totalnum > 0} { + chan seek $chan $cdiroffset start + if {[read $chan 4] ne "\x50\x4b\x01\x02"} { + return [dict create status reject] + } + } elseif {$cdirsize != 0} { + return [dict create status reject] + } + #The recorded directory offset is either archive-relative (the directory sits + #that far past a preamble) or file-relative (it IS the file position). The + #difference between where the directory actually is and where the record says + #it is therefore gives the preamble length directly; negative means neither + #reading holds and this candidate is not a real EOCD. + set offsetbase [expr {$cdiroffset - $diroffset}] + if {$offsetbase < 0} { + return [dict create status reject] + } + set comment "" + if {$commentlen > 0} { + chan seek $chan [expr {$eocdoffset + 22}] start + set comment [read $chan $commentlen] + } + dict set info cdiroffset $cdiroffset + dict set info offsetbase $offsetbase + dict set info comment $comment + return $info + } + + punk::args::define { + @id -id ::punk::zip::Cdir_records + @cmd -name punk::zip::Cdir_records\ + -summary\ + "Parse every central directory record of an archive"\ + -help\ + "Walk ALL central directory file headers and return one member dict per + entry, in stored order. Sizes and offsets come from the central + directory, which sidesteps data descriptors entirely. + See punk::zip::members for the member dict keys." + @values -min 2 -max 2 + chan -help "open binary channel positioned anywhere in the archive" + info -type dict -help "archive dict from Eocd_scan with status ok" + } + proc Cdir_records {chan info} { + set cdiroffset [dict get $info cdiroffset] + set cdirsize [dict get $info cdirsize] + set count [dict get $info count] + set offsetbase [dict get $info offsetbase] + if {$count == 0} { + return [list] + } + chan seek $chan $cdiroffset start + set cd [read $chan $cdirsize] + if {[string length $cd] != $cdirsize} { + error "punk::zip: central directory truncated - wanted $cdirsize bytes at $cdiroffset, got [string length $cd]" + } + set members [list] + set pos 0 + for {set i 0} {$i < $count} {incr i} { + if {$pos + 46 > $cdirsize} { + error "punk::zip: central directory truncated - record [expr {$i + 1}] of $count starts beyond the directory" + } + binary scan $cd @${pos}issssssiiisssssii sig madeby version flags method dostime dosdate crc csize size namelen extralen commentlen disknbr iattr eattr offset + if {$sig != 33639248} { + error "punk::zip: bad central directory record [expr {$i + 1}] of $count - expected signature PK\\1\\2" + } + set madeby [expr {$madeby & 0xFFFF}] + set version [expr {$version & 0xFFFF}] + set flags [expr {$flags & 0xFFFF}] + set method [expr {$method & 0xFFFF}] + set dostime [expr {$dostime & 0xFFFF}] + set dosdate [expr {$dosdate & 0xFFFF}] + set namelen [expr {$namelen & 0xFFFF}] + set extralen [expr {$extralen & 0xFFFF}] + set commentlen [expr {$commentlen & 0xFFFF}] + set iattr [expr {$iattr & 0xFFFF}] + set crc [expr {$crc & 0xFFFFFFFF}] + set csize [expr {$csize & 0xFFFFFFFF}] + set size [expr {$size & 0xFFFFFFFF}] + set eattr [expr {$eattr & 0xFFFFFFFF}] + set offset [expr {$offset & 0xFFFFFFFF}] + set namestart [expr {$pos + 46}] + set rawname [string range $cd $namestart [expr {$namestart + $namelen - 1}]] + set commentstart [expr {$namestart + $namelen + $extralen}] + set rawcomment [string range $cd $commentstart [expr {$commentstart + $commentlen - 1}]] + set packedtime [expr {($dosdate << 16) | $dostime}] + set name [Decode_text $rawname $flags] + set hostsystem [expr {($madeby >> 8) & 0xFF}] + lappend members [dict create {*}{ + } name $name {*}{ + } isdirectory [Is_directory_entry $name $size $eattr $hostsystem] {*}{ + } size $size {*}{ + } csize $csize {*}{ + } method $method {*}{ + } methodname [Method_name $method] {*}{ + } mtime [Dos_to_timet $packedtime] {*}{ + } dostime $packedtime {*}{ + } crc $crc {*}{ + } offset $offset {*}{ + } fileoffset [expr {$offsetbase + $offset}] {*}{ + } flags $flags {*}{ + } encrypted [expr {($flags & 0x41) != 0}] {*}{ + } attributes $eattr {*}{ + } iattributes $iattr {*}{ + } madeby $madeby {*}{ + } hostsystem $hostsystem {*}{ + } version $version {*}{ + } comment [Decode_text $rawcomment $flags] {*}{ + }] + incr pos [expr {46 + $namelen + $extralen + $commentlen}] + } + return $members + } + + #Member names and comments are utf-8 when general purpose bit 11 is set + #(punk::zip::mkzip always sets it), and cp437 otherwise. + proc Decode_text {bytes flags} { + if {$bytes eq ""} { + return "" + } + if {$flags & 0x800} { + return [encoding convertfrom utf-8 $bytes] + } + if {[catch {encoding convertfrom cp437 $bytes} decoded]} { + set decoded [encoding convertfrom iso8859-1 $bytes] + } + return $decoded + } + + #A trailing slash is the portable marker; the FAT directory attribute and the + #unix S_IFDIR mode bits are accepted as secondary evidence for archives that + #store directory entries without one. + proc Is_directory_entry {name size eattr hostsystem} { + if {[string index $name end] eq "/"} { + return 1 + } + if {$hostsystem == 3 && (($eattr >> 16) & 0xF000) == 0x4000} { + return 1 + } + if {$size == 0 && ($eattr & 0x10)} { + return 1 + } + return 0 + } + + punk::args::define { + @id -id ::punk::zip::Archive_read + @cmd -name punk::zip::Archive_read\ + -summary\ + "Read the structure of a zip archive on an open channel"\ + -help\ + "Derive the archive geometry and read every central directory record. + Returns a 2-element dict: 'info' (see punk::zip::archive_info for the + keys) and 'members' (see punk::zip::members). + + Never raises for a file that simply is not an archive - callers decide + what a non-ok status means. Does raise for an archive whose central + directory is truncated or malformed." + @values -min 1 -max 1 + chan -help "open channel - reconfigured to binary translation by this call" + } + proc Archive_read {chan} { + chan configure $chan -translation binary + chan seek $chan 0 end + set filesize [tell $chan] + set info [Eocd_scan $chan $filesize] + if {[dict get $info status] ne "ok"} { + #the whole file is preamble as far as any splitting caller is concerned + dict set info dataoffset $filesize + dict set info offsetstyle none + return [dict create info $info members {}] + } + set members [Cdir_records $chan $info] + set offsetbase [dict get $info offsetbase] + set minoffset "" + foreach m $members { + set o [dict get $m offset] + if {$minoffset eq "" || $o < $minoffset} { + set minoffset $o + } + } + if {$minoffset eq ""} { + set minoffset 0 + } + if {$offsetbase > 0} { + #external preamble, archive-relative offsets: the archive begins exactly + #where its own offsets say it does + set dataoffset $offsetbase + set offsetstyle archive + } else { + #file-relative offsets (or no preamble at all): the archive begins at the + #topmost local file header the directory points to. Looking at ALL records + #rather than just the first is what makes this reliable - records are in no + #guaranteed order. + set dataoffset $minoffset + set offsetstyle [expr {$minoffset > 0 ? "file" : "plain"}] + } + dict set info dataoffset $dataoffset + dict set info offsetstyle $offsetstyle + if {[llength $members]} { + #the derivation is only trustworthy if a local file header really sits + #where the first record says it does + chan seek $chan [expr {$offsetbase + $minoffset}] start + if {[read $chan 4] ne "\x50\x4b\x03\x04"} { + dict set info status unsupported + dict set info reason "no local file header at derived archive position [expr {$offsetbase + $minoffset}] - offsets do not describe this file" + } + } + return [dict create info $info members $members] + } + + #if there is an external preamble - extract that. (if there is also an internal preamble - ignore and consider part of the archive-data) + #Otherwise extract an internal preamble. + #if neither -? + #review - reconsider auto-determination of internal vs external preamble + punk::args::define { + @id -id ::punk::zip::extract_preamble + @cmd -name punk::zip::extract_preamble -help\ + "Split a zipfs based executable or library into its constituent + binary and zip parts. + + Note that the binary preamble might be either 'within' the zip offsets, + or simply catenated prior to an unadjusted zip. + Some build processes may have 'adjusted' the zip offsets to make the zip cover the entire file + ('file based' offset) whilst the more modern approach is to simply concatenate the binary and the zip + ('archive based' offset). An archive-based offset is simpler and more reliably points to the proper + split location. It also allows 'zipfs info //zipfs:/app' to return the correct offset information. + + Either way, extract_preamble can usually separate them, but in the unusual case that there is both an + external preamble and a preamble within the zip, only the external preamble will be split, with the + internal one remaining in the zip. + + The inverse of this process would be to extract the .zip file created by this split to a folder, + e.g extracted_zip_folder (adjusting contents as required) and then to run: + zipfs mkimg newbinaryname.exe extracted_zip_folder \"\" + " + @values -min 2 -max 3 + infile -type file -optional 0 -help\ + "Name of existing tcl executable or shared lib with attached zipfs filesystem" + outfile_preamble -optional 0 -type file -help\ + "Name of output file for binary preamble to be extracted to. + If this file already exists, an error will be raised" + outfile_zip -default "" -type file -help\ + "Name of output file for zip data to be extracted to. + If this file already exists, an error will be raised" + } + proc extract_preamble {args} { + set argd [punk::args::parse $args withid ::punk::zip::extract_preamble] + lassign [dict values $argd] leaders opts values received + + set infile [dict get $values infile] + set outfile_preamble [dict get $values outfile_preamble] + set outfile_zip [dict get $values outfile_zip] + + set inzip [open $infile r] + fconfigure $inzip -encoding iso8859-1 -translation binary + if {[file exists $outfile_preamble]} { + error "outfile_preamble $outfile_preamble already exists - please remove first" + } + if {$outfile_zip ne ""} { + if {[file exists $outfile_zip] && [file size $outfile_zip]} { + error "outfile_zip $outfile_zip already exists - please remove first" + } + } + chan seek $inzip 0 end + set insize [tell $inzip] ;#faster (including seeks) than calling out to filesystem using file size - but should be equivalent + + #Archive_read does the whole derivation: it finds the EOCD (rejecting the + #PK\5\6 byte sequences that occur in plain executables), decides whether the + #recorded offsets are archive-relative or file-relative, and walks ALL central + #directory records to find the topmost local file header in the file-relative + #case. dataoffset is the split point either way. + #expect CDFH PK\1\2 + #above the CD - we expect a bunch of PK\3\4 records - (possibly not all of them pointed to by the CDR) + #above that we expect: *possibly* a stored password with trailing marker - then the prefixed exe/script + set info [dict get [Archive_read $inzip] info] + switch -- [dict get $info status] { + ok { + set baseoffset [dict get $info dataoffset] + } + nozip { + #no zip eocdr - consider entire file to be the zip preamble + set baseoffset $insize + } + default { + close $inzip + error "unable to determine zip baseoffset of file $infile - [dict get $info reason]" + } + } + + if {$baseoffset < $insize} { + set pout [open $outfile_preamble w] + fconfigure $pout -encoding iso8859-1 -translation binary + chan seek $inzip 0 start + chan copy $inzip $pout -size $baseoffset + close $pout + if {$outfile_zip ne ""} { + #A file-relative archive splits into a .zip whose offsets still count + #from the removed preamble - readers that only accept a bare .zip choke + #on it. punk::zip's own reader never needs the split: point it at the + #ORIGINAL file and the derived base offset makes the shape irrelevant. + #Rewriting the offsets here stays open for callers that must hand a + #plain .zip to something else. + set zout [open $outfile_zip w] + fconfigure $zout -encoding iso8859-1 -translation binary + chan copy $inzip $zout + close $zout + } + close $inzip + } else { + #no valid (from our perspective) eocdr found - baseoffset has been set to insize + close $inzip + file copy $infile $outfile_preamble + if {$outfile_zip ne ""} { + #touch equiv? + set fd [open $outfile_zip w] + close $fd + } + } + } + + # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + # Reading - archive_info / members / unzip + # + # Stock Tcl only: no zipfs, no vfs::zip, no tcllib. Works on a plain .zip and on + # a zip attached to an executable or script prefix, under either offset + # convention, because everything reads the ORIGINAL file at a derived base + # offset rather than a split-off intermediate. + # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + + #open + read structure + close, for the surfaces that take a filename + proc Open_archive {zipfile requirezip} { + if {![file exists $zipfile]} { + error "punk::zip: no such file '$zipfile'" + } + set chan [open $zipfile rb] + if {[catch {Archive_read $chan} arc erropts]} { + close $chan + return -options $erropts $arc + } + set info [dict get $arc info] + set status [dict get $info status] + if {$requirezip && $status ne "ok"} { + close $chan + switch -- $status { + nozip { + error "punk::zip: '$zipfile' is not a zip archive - [dict get $info reason]" + } + default { + error "punk::zip: cannot read '$zipfile' - [dict get $info reason]" + } + } + } + return [dict set arc chan $chan] + } + + #glob selection shared by members and unzip. Patterns are matched against the + #member name both as stored and with any trailing slash removed, so a pattern + #like lib/* selects the lib/ directory entry as well as its contents. + proc Select_members {members globs excludes} { + set selected [list] + foreach m $members { + set name [dict get $m name] + set bare [string trimright $name /] + set matched 0 + foreach g $globs { + if {[string match $g $name] || [string match $g $bare]} { + set matched 1 + break + } + } + if {!$matched} { + continue + } + set excluded 0 + foreach e $excludes { + if {[string match $e $name] || [string match $e $bare]} { + set excluded 1 + break + } + } + if {!$excluded} { + lappend selected $m + } + } + return $selected + } + + #Member names are archive data, not trusted paths: a name that escapes the target + #directory is refused rather than followed. Backslashes are normalised to forward + #slashes (some writers emit them) so the check cannot be side-stepped on windows. + proc Safe_member_path {name} { + set segments [list] + foreach seg [split [string map {\\ /} $name] /] { + if {$seg eq "" || $seg eq "."} { + continue + } + if {$seg eq ".."} { + error "punk::zip: refusing member '$name' - parent-directory segment would escape the target directory" + } + lappend segments $seg + } + if {![llength $segments]} { + error "punk::zip: refusing member '$name' - empty path" + } + if {[file pathtype [lindex $segments 0]] ne "relative"} { + error "punk::zip: refusing member '$name' - not a relative path" + } + return $segments + } + + #Reasons punk::zip cannot extract a member. Checked for every selected member + #BEFORE anything is written, so an unsupported archive produces an error instead + #of a partly-populated target directory. + proc Member_unsupported_reason {m} { + if {[dict get $m encrypted]} { + return "entry is encrypted - punk::zip cannot decrypt zip entries" + } + if {[dict get $m size] == 0xFFFFFFFF || [dict get $m csize] == 0xFFFFFFFF || [dict get $m offset] == 0xFFFFFFFF} { + return "entry uses zip64 size/offset fields - punk::zip reads zip32 entries only" + } + set method [dict get $m method] + if {$method ni {0 8}} { + return "entry uses compression method $method ([dict get $m methodname]) - punk::zip reads stored (0) and deflated (8) entries only" + } + return "" + } + + #Read one member's data from the archive and write it to target. + #Sizes and offsets come from the central directory, so entries written with a + #data descriptor (streamed, sizes zero in the local header) read correctly. + proc Extract_member {chan m target verify} { + set name [dict get $m name] + set fileoffset [dict get $m fileoffset] + set csize [dict get $m csize] + set size [dict get $m size] + set method [dict get $m method] + chan seek $chan $fileoffset start + set lfh [read $chan 30] + if {[string length $lfh] != 30} { + error "punk::zip: truncated local file header for member '$name' at offset $fileoffset" + } + binary scan $lfh isssssiiiss lsig lversion lflags lmethod ltime ldate lcrc lcsize lsize lnamelen lextralen + #dec2hex 67324752 = 4034B50 = PK\3\4 + if {$lsig != 67324752} { + error "punk::zip: no local file header for member '$name' at offset $fileoffset - archive offsets do not describe this file" + } + set lnamelen [expr {$lnamelen & 0xFFFF}] + set lextralen [expr {$lextralen & 0xFFFF}] + chan seek $chan [expr {$fileoffset + 30 + $lnamelen + $lextralen}] start + set out [open $target wb] + set crc 0 + set written 0 + try { + if {$size < 0x00200000} { + #small member - one chunk, mirroring Addentry's 2MB write threshold + set data "" + if {$csize > 0} { + set data [read $chan $csize] + if {[string length $data] != $csize} { + error "punk::zip: truncated data for member '$name' - wanted $csize bytes, got [string length $data]" + } + if {$method == 8} { + set data [zlib inflate $data] + } + } + set crc [zlib crc32 $data] + set written [string length $data] + puts -nonewline $out $data + } else { + #large member - stream, to avoid holding the whole thing in memory + set strm "" + if {$method == 8} { + set strm [zlib stream inflate] + } + set remaining $csize + while {$remaining > 0} { + set want [expr {$remaining > 65536 ? 65536 : $remaining}] + set chunk [read $chan $want] + if {[string length $chunk] != $want} { + error "punk::zip: truncated data for member '$name' - archive ends mid-entry" + } + incr remaining -$want + if {$strm eq ""} { + set crc [zlib crc32 $chunk $crc] + incr written [string length $chunk] + puts -nonewline $out $chunk + continue + } + if {$remaining == 0} { + $strm put -finalize $chunk + } else { + $strm put $chunk + } + #a single get returns only what the stream has already produced - + #drain until empty or the tail of the member is silently dropped + while {1} { + set plain [$strm get 65536] + if {$plain eq ""} { + break + } + set crc [zlib crc32 $plain $crc] + incr written [string length $plain] + puts -nonewline $out $plain + } + } + if {$strm ne ""} { + $strm close + } + } + close $out + set out "" + if {$written != $size} { + error "punk::zip: size mismatch for member '$name' - central directory says $size bytes, decompressed to $written" + } + if {$verify && ($crc & 0xFFFFFFFF) != [dict get $m crc]} { + error "punk::zip: crc mismatch for member '$name' - archive records [dict get $m crc], extracted data is [expr {$crc & 0xFFFFFFFF}]" + } + } on error {result erropts} { + if {$out ne ""} { + catch {close $out} + } + #never leave unverified bytes behind + catch {file delete -- $target} + return -options $erropts $result + } + return $written + } + + punk::args::define { + @id -id ::punk::zip::archive_info + @cmd -name punk::zip::archive_info\ + -summary\ + "Report the zip-archive geometry of a file"\ + -help\ + "Report where the zip archive inside a file begins and which offset + convention it uses, without extracting anything. + + The file may be a plain .zip or a zip attached to an executable or script + prefix - a punk kit, a tclsh carrying a zipfs image, a zip-based .tm + modpod. This is the derivation punk::zip::members, punk::zip::unzip and + punk::zip::extract_preamble all share. + + Does not raise for a file that simply is not an archive - read 'status'. + + Returned dict keys: + status ok | nozip | unsupported + reason why status is not ok + filesize size of the whole file + offsetstyle plain | archive | file | none + plain - a bare zip, no prefix + archive - prefixed, offsets counted from the zip + file - prefixed, offsets counted from the file + none - no archive found + dataoffset where the zip data starts, ie the prefix length + offsetbase add this to a member's stored offset for a file position + eocdoffset file position of the end-of-central-directory record + cdiroffset file position of the central directory + cdirsize size of the central directory + count number of members + diroffset central directory offset as recorded in the archive + comment archive comment + + Examples: + #which convention is this runtime using? + dict get [punk::zip::archive_info bin/punk91.exe] offsetstyle + + #how big is the executable prefix? + dict get [punk::zip::archive_info bin/punk91.exe] dataoffset + + #is there an archive attached at all? (status is ok when there is) + dict get [punk::zip::archive_info some.exe] status + " + @values -min 1 -max 1 + zipfile -type existingfile -help\ + "Path of a zip archive, or of a file with one attached" + } + proc archive_info {args} { + set argd [punk::args::parse $args withid ::punk::zip::archive_info] + set zipfile [dict get $argd values zipfile] + set arc [Open_archive $zipfile 0] + close [dict get $arc chan] + return [dict get $arc info] + } + + punk::args::define { + @id -id ::punk::zip::members + @cmd -name punk::zip::members\ + -summary\ + "List the members of a zip archive without extracting"\ + -help\ + "Read the central directory of a zip archive and return one dict per + member, in stored order, without decompressing anything. + + The archive may be a plain .zip or a zip attached to an executable or + script prefix - see punk::zip::archive_info. Requires only stock Tcl: no + zipfs, no vfs::zip, no tcllib. + + Entries punk::zip cannot EXTRACT (encrypted, zip64, an unsupported + compression method) are still listed - the refusal belongs to + punk::zip::unzip, which names the reason. + + Each member dict carries: + name member path as stored (directories keep a trailing /) + isdirectory 1 for a directory entry, else 0 + size uncompressed size in bytes + csize compressed size in bytes + method compression method id (0 store, 8 deflate) + methodname symbolic name for that method + mtime modification time as a unix timestamp (0 if unusable) + dostime the packed DOS date/time exactly as stored + crc crc32 of the uncompressed data + offset local header offset as stored in the central directory + fileoffset absolute position of that local header in the file + flags general purpose bit flag + encrypted 1 if the entry is encrypted + attributes external file attributes as stored + iattributes internal file attributes as stored + madeby version-made-by field + hostsystem host system code (upper byte of madeby - 0 fat, 3 unix) + version version needed to extract + comment per-member comment + + Examples: + #everything inside a kit + punk::zip::members bin/punk91.exe + + #just the tcl_library scripts + punk::zip::members bin/punk91.exe tcl_library/*.tcl + + #total uncompressed size + set bytes 0 + foreach m [punk::zip::members some.zip] {incr bytes [dict get \$m size]} + + #names only, files not directories + set names [list] + foreach m [punk::zip::members some.zip] { + if {![dict get \$m isdirectory]} {lappend names [dict get \$m name]} + } + " + @opts + -exclude -default {} -help\ + "List of glob expressions matched against member names. + A member matching any of them is left out of the result." + -- -type none -optional 1 -help\ + "End of options marker" + @values -min 1 -max -1 + zipfile -type existingfile -help\ + "Path of a zip archive, or of a file with one attached" + globs -default {*} -multiple 1 -help\ + "Glob patterns matched against member names with 'string match'. + A member is included if it matches any of them. + The whole stored name is matched, so * spans path separators: + *.txt selects sub/deeper/notes.txt as well as top.txt. + Directory entries match with or without their trailing slash." + } + proc members {args} { + set argd [punk::args::parse $args withid ::punk::zip::members] + set zipfile [dict get $argd values zipfile] + set globs [dict get $argd values globs] + set excludes [dict get $argd opts -exclude] + set arc [Open_archive $zipfile 1] + close [dict get $arc chan] + return [Select_members [dict get $arc members] $globs $excludes] + } + + punk::args::define { + @id -id ::punk::zip::unzip + @cmd -name punk::zip::unzip\ + -summary\ + "Extract a zip archive to a directory"\ + -help\ + "Extract all or part of a zip archive into targetdir, verifying each + member's crc32 as it is written. Directory entries are created as + directories; intermediate directories of a selected file are created + whether or not the archive stores an entry for them. + + The archive may be a plain .zip or a zip attached to an executable or + script prefix - see punk::zip::archive_info. Requires only stock Tcl: no + zipfs, no vfs::zip, no tcllib. + + Every selected member is checked for extractability BEFORE anything is + written, so an encrypted, zip64 or unknown-compression archive fails + naming the reason instead of leaving a half-populated directory. A + member whose path would escape targetdir is refused the same way. + + Returns the list of member names extracted (see -return). + + Examples: + #whole archive + punk::zip::unzip some.zip /tmp/out + + #lift a runtime's tcl_library out of the executable it is attached to + punk::zip::unzip bin/punk91.exe /tmp/rt tcl_library/* + + #everything except the docs, keeping timestamps + punk::zip::unzip -exclude {doc/*} -- some.zip /tmp/out + " + @opts + -exclude -default {} -help\ + "List of glob expressions matched against member names. + A member matching any of them is not extracted." + -overwrite -default 1 -type boolean -help\ + "Whether an existing file in targetdir may be replaced. + With -overwrite 0 an existing target is an error, raised before + anything is written." + -mtime -default 1 -type boolean -help\ + "Whether to restore each member's stored modification time. + Directory times are applied after their contents are written." + -verify -default 1 -type boolean -help\ + "Whether to check each extracted member against the crc32 stored in + the archive. A mismatch is an error and the partial file is removed." + -return -default list -choices {list pretty none} -help\ + "list - return the extracted member names (default) + pretty - format that list for terminal display + none - return the empty string" + -- -type none -optional 1 -help\ + "End of options marker" + @values -min 2 -max -1 + zipfile -type existingfile -help\ + "Path of a zip archive, or of a file with one attached" + targetdir -type directory -help\ + "Directory to extract into. Created if it does not exist." + globs -default {*} -multiple 1 -help\ + "Glob patterns matched against member names with 'string match'. + A member is extracted if it matches any of them. + The whole stored name is matched, so * spans path separators: + *.txt selects sub/deeper/notes.txt as well as top.txt. + Directory entries match with or without their trailing slash." + } + proc unzip {args} { + set argd [punk::args::parse $args withid ::punk::zip::unzip] + set zipfile [dict get $argd values zipfile] + set targetdir [dict get $argd values targetdir] + set globs [dict get $argd values globs] + set excludes [dict get $argd opts -exclude] + set overwrite [dict get $argd opts -overwrite] + set restoremtime [dict get $argd opts -mtime] + set verify [dict get $argd opts -verify] + + set arc [Open_archive $zipfile 1] + set chan [dict get $arc chan] + set extracted [list] + set dirtimes [list] + try { + set selected [Select_members [dict get $arc members] $globs $excludes] + #preflight - nothing is written until every selected member is known good + set targets [list] + foreach m $selected { + set reason [Member_unsupported_reason $m] + if {$reason ne ""} { + error "punk::zip::unzip: cannot extract '[dict get $m name]' from $zipfile - $reason" + } + set target [file join $targetdir {*}[Safe_member_path [dict get $m name]]] + if {!$overwrite && !([dict get $m isdirectory]) && [file exists $target]} { + error "punk::zip::unzip: '$target' already exists and -overwrite is 0" + } + lappend targets $target + } + file mkdir $targetdir + foreach m $selected target $targets { + set name [dict get $m name] + if {[dict get $m isdirectory]} { + file mkdir $target + if {$restoremtime && [dict get $m mtime]} { + lappend dirtimes $target [dict get $m mtime] + } + } else { + file mkdir [file dirname $target] + Extract_member $chan $m $target $verify + if {$restoremtime && [dict get $m mtime]} { + catch {file mtime $target [dict get $m mtime]} + } + } + lappend extracted $name + } + } finally { + close $chan + } + #directory times last - writing their contents would have reset them + foreach {dir mtime} $dirtimes { + catch {file mtime $dir $mtime} + } + + switch -exact -- [dict get $argd opts -return] { + pretty { + if {[info commands showlist] ne ""} { + return [plist -channel none extracted] + } + return $extracted + } + none { + return "" + } + default { + return $extracted + } + } + } + + + + punk::args::define { + @id -id ::punk::zip::Addentry + @cmd -name punk::zip::Addentry\ + -summary\ + "Add zip-entry for file at 'path'"\ + -help\ + "Add a single file at 'path' to open channel 'zipchan' + return a central directory file record" + @opts + -comment -default "" -help "An optional comment specific to the added file" + @values -min 3 -max 4 + zipchan -help "open file descriptor with cursor at position appropriate for writing a local file header" + base -help "base path for entries" + path -type file -help "path of file to add" + zipdataoffset -default 0 -type integer -range {0 ""} -help "offset of start of zip-data - ie length of prefixing script/exe + Can be specified as zero even if a prefix exists - which would make offsets 'file relative' as opposed to 'archive relative'" + } + + # Addentry - was Mkzipfile -- + # + # FIX ME: should handle the current offset for non-seekable channels + # + proc Addentry {args} { + #*** !doctools + #[call [fun Addentry] [arg zipchan] [arg base] [arg path] [arg ?comment?]] + #[para] Add a single file to a zip archive + #[para] The zipchan channel should already be open and binary. + #[para] You can provide a -comment for the file. + #[para] The return value is the central directory record that will need to be used when finalizing the zip archive. + + set argd [punk::args::parse $args withid ::punk::zip::Addentry] + set zipchan [dict get $argd values zipchan] + set base [dict get $argd values base] + set path [dict get $argd values path] + set zipdataoffset [dict get $argd values zipdataoffset] + + set comment [dict get $argd opts -comment] + + set fullpath [file join $base $path] + set mtime [Timet_to_dos [file mtime $fullpath]] + set utfpath [encoding convertto utf-8 $path] + set utfcomment [encoding convertto utf-8 $comment] + set flags [expr {(1<<11)}] ;# utf-8 comment and path + set method 0 ;# store 0, deflate 8 + set attr 0 ;# text or binary (default binary) + set version 20 ;# minumum version req'd to extract + set extra "" + set crc 0 + set size 0 + set csize 0 + set data "" + set seekable [expr {[tell $zipchan] != -1}] + if {[file isdirectory $fullpath]} { + set attrex 0x41ff0010 ;# 0o040777 (drwxrwxrwx) + #set attrex 0x40000010 + } elseif {[file executable $fullpath]} { + set attrex 0x81ff0080 ;# 0o100777 (-rwxrwxrwx) + } else { + set attrex 0x81b60020 ;# 0o100666 (-rw-rw-rw-) + if {[file extension $fullpath] in {".tcl" ".txt" ".c"}} { + set attr 1 ;# text + } + } + + if {[file isfile $fullpath]} { + set size [file size $fullpath] + if {!$seekable} {set flags [expr {$flags | (1 << 3)}]} + } + + + set channeloffset [tell $zipchan] ;#position in the channel - this may include prefixing exe/zip + set local [binary format a4sssiiiiss PK\03\04 \ + $version $flags $method $mtime $crc $csize $size \ + [string length $utfpath] [string length $extra]] + append local $utfpath $extra + puts -nonewline $zipchan $local + + if {[file isfile $fullpath]} { + # If the file is under 2MB then zip in one chunk, otherwize we use + # streaming to avoid requiring excess memory. This helps to prevent + # storing re-compressed data that may be larger than the source when + # handling PNG or JPEG or nested ZIP files. + if {$size < 0x00200000} { + set fin [open $fullpath rb] + set data [read $fin] + set crc [zlib crc32 $data] + set cdata [zlib deflate $data] + if {[string length $cdata] < $size} { + set method 8 + set data $cdata + } + close $fin + set csize [string length $data] + puts -nonewline $zipchan $data + } else { + set method 8 + set fin [open $fullpath rb] + set zlib [zlib stream deflate] + while {![eof $fin]} { + set data [read $fin 4096] + set crc [zlib crc32 $data $crc] + $zlib put $data + if {[string length [set zdata [$zlib get]]]} { + incr csize [string length $zdata] + puts -nonewline $zipchan $zdata + } + } + close $fin + $zlib finalize + set zdata [$zlib get] + incr csize [string length $zdata] + puts -nonewline $zipchan $zdata + $zlib close + } + + if {$seekable} { + # update the header if the output is seekable + set local [binary format a4sssiiii PK\03\04 \ + $version $flags $method $mtime $crc $csize $size] + set current [tell $zipchan] + seek $zipchan $channeloffset + puts -nonewline $zipchan $local + seek $zipchan $current + } else { + # Write a data descriptor record + set ddesc [binary format a4iii PK\7\8 $crc $csize $size] + puts -nonewline $zipchan $ddesc + } + } + + #PK\x01\x02 Cdentral directory file header + #set v1 0x0317 ;#upper byte 03 -> UNIX lower byte 23 -> 2.3 + set v1 0x0017 ;#upper byte 00 -> MS_DOS and OS/2 (FAT/VFAT/FAT32 file systems) + + set hdr [binary format a4ssssiiiisssssii PK\01\02 $v1 \ + $version $flags $method $mtime $crc $csize $size \ + [string length $utfpath] [string length $extra]\ + [string length $utfcomment] 0 $attr $attrex [expr {$channeloffset - $zipdataoffset}]] ;#zipdataoffset may be zero - either because it's a pure zip, or file-based offsets desired. + append hdr $utfpath $extra $utfcomment + return $hdr + } + + #### REVIEW!!! + #JMN - review - this looks to be offset relative to start of file - (same as 2024 Tcl 'mkzip mkimg') + # we want to enable (optionally) offsets relative to start of archive for exe/script-prefixed zips.on windows (editability with 7z,peazip) + #### + + + punk::args::define { + @id -id ::punk::zip::mkzip + @cmd -name punk::zip::mkzip\ + -summary\ + "Create a zip archive in 'filename'."\ + -help\ + "Create a zip archive in 'filename'" + @opts + -offsettype -default "archive" -choices {archive file}\ + -help\ + "zip offsets stored relative to start of entire file or relative to start of zip-archive + Only relevant if the created file has a script/runtime prefix." + -return -default "pretty" -choices {pretty list none}\ + -help\ + "mkzip can return a list of the files and folders added to the archive + the option -return pretty is the default and uses the punk::lib pdict/plist system + to return a formatted list for the terminal + " + -zipkit -default 0 -type none\ + -help\ + "whether to add mounting script + mutually exclusive with -runtime option + currently vfs::zip based - todo - autodetect zipfs/vfs with pref for zipfs" + -runtime -default ""\ + -help\ + "specify a prefix file + e.g punk::zip::mkzip -runtime unzipsfx.exe -directory subdir -base subdir output.zip + will create a self-extracting zip archive from the subdir/ folder. + Expects runtime with no existing vfs attached (review)" + -comment -default ""\ + -help "An optional comment for the archive" + -directory -default ""\ + -help "Scan for contents within this folder or current directory if not provided." + -base -default ""\ + -help\ + "The new zip archive will be rooted in this directory if provided + it must be a parent of -directory or the same path as -directory" + -exclude -default {CVS/* */CVS/* *~ ".#*" "*/.#*"} + -- -type none -help\ + "End of options marker" + + @values -min 1 -max -1 + filename -type file -default ""\ + -help "name of zipfile to create" + globs -default {*} -multiple 1\ + -help\ + "list of glob patterns to match. + Only directories with matching files will be included in the archive." + } + + # zip::mkzip -- + # + # eg: zip my.zip -directory Subdir -runtime unzipsfx.exe *.txt + # + proc mkzip {args} { + #todo - doctools - [arg ?globs...?] syntax? + + #*** !doctools + #[call [fun mkzip]\ + # [opt "[option -offsettype] [arg offsettype]"]\ + # [opt "[option -return] [arg returntype]"]\ + # [opt "[option -zipkit] [arg 0|1]"]\ + # [opt "[option -runtime] [arg preamble_filename]"]\ + # [opt "[option -comment] [arg zipfilecomment]"]\ + # [opt "[option -directory] [arg dir_to_zip]"]\ + # [opt "[option -base] [arg archive_root]"]\ + # [opt "[option -exclude] [arg globlist]"]\ + # [arg zipfilename]\ + # [arg ?glob...?]] + #[para] Create a zip archive in 'zipfilename' + #[para] If a file already exists, an error will be raised. + #[para] Call 'punk::zip::mkzip' with no arguments for usage display. + + set argd [punk::args::parse $args withid ::punk::zip::mkzip] + set filename [dict get $argd values filename] + if {$filename eq ""} { + error "mkzip filename cannot be empty string" + } + if {[regexp {[?*]} $filename]} { + #catch a likely error where filename is omitted and first glob pattern is misinterpreted as zipfile name + error "mkzip filename should not contain glob characters ? *" + } + if {[file exists $filename]} { + error "mkzip filename:$filename already exists" + } + dict for {k v} [dict get $argd opts] { + switch -- $k { + -comment { + dict set argd opts $k [encoding convertto utf-8 $v] + } + -directory - -base { + dict set argd opts $k [file normalize $v] + } + } + } + + array set opts [dict get $argd opts] + + + if {$opts(-directory) ne ""} { + if {$opts(-base) ne ""} { + #-base and -directory have been normalized already + if {![Path_a_atorbelow_b $opts(-directory) $opts(-base)]} { + error "punk::zip::mkzip -base $opts(-base) must be above or the same as -directory $opts(-directory)" + } + set base $opts(-base) + set relpath [Path_strip_alreadynormalized_prefixdepth $opts(-directory) $opts(-base)] + } else { + set base $opts(-directory) + set relpath "" + } + #will pick up intermediary folders as paths (ending with trailing slash) + set paths [walk -exclude $opts(-exclude) -subpath $relpath -- $base {*}[dict get $argd values globs]] + + set norm_filename [file normalize $filename] + set norm_dir [file normalize $opts(-directory)] ;#we only care if filename below -directory (which is where we start scanning) + if {[Path_a_atorbelow_b $norm_filename $norm_dir]} { + #check that we aren't adding the zipfile to itself + #REVIEW - now that we open zipfile after scanning - this isn't really a concern! + #keep for now in case we can add an -update or a -force facility (or in case we modify to add to zip as we scan for members?) + #In the case of -force - we may want to delay replacement of original until scan is done? + + #try to avoid looping on all paths and performing (somewhat) expensive file normalizations on each + #1st step is to check the patterns and see if our zipfile is already excluded - in which case we need not check the paths + set self_globs_match 0 + foreach g [dict get $argd values globs] { + if {[string match $g [file tail $filename]]} { + set self_globs_match 1 + break + } + } + if {$self_globs_match} { + #still dangerous + set self_excluded 0 + foreach e $opts(-exclude) { + if {[string match $e [file tail $filename]]} { + set self_excluded 1 + break + } + } + if {!$self_excluded} { + #still dangerous - likely to be in resultset - check each path + #puts stderr "zip file $filename is below directory $opts(-directory)" + set self_is_matched 0 + set i 0 + foreach p $paths { + set norm_p [file normalize [file join $opts(-directory) $p]] + if {[Path_a_at_b $norm_filename $norm_p]} { + set self_is_matched 1 + break + } + incr i + } + if {$self_is_matched} { + puts stderr "WARNING - zipfile being created '$filename' was matched. Excluding this file. Relocate the zip, or use -exclude patterns to avoid this message" + set paths [lremove $paths $i] + } + } + } + } + } else { + #NOTE that we don't add intermediate folders when creating an archive without using the -directory flag! + #ie - only the exact *files* matching the glob are stored. + set paths [list] + set dir [pwd] + if {$opts(-base) ne ""} { + if {![Path_a_atorbelow_b $dir $opts(-base)]} { + error "punk::zip::mkzip -base $opts(-base) must be above current directory" + } + set relpath [Path_strip_alreadynormalized_prefixdepth [file normalize $dir] [file normalize $opts(-base)]] + } else { + set relpath "" + } + set base $opts(-base) + + set matches [glob -nocomplain -type f -- {*}[dict get $argd values globs]] + foreach m $matches { + if {$m eq $filename} { + #puts stderr "--> excluding $filename" + continue + } + set isok 1 + foreach e [concat $opts(-exclude) $filename] { + if {[string match $e $m]} { + set isok 0 + break + } + } + if {$isok} { + lappend paths [file join $relpath $m] + } + } + } + + if {![llength $paths]} { + return "" + } + + set zf [open $filename wb] + if {$opts(-runtime) ne ""} { + #todo - strip any existing vfs - option to merge contents.. only if zip attached? + set rt [open $opts(-runtime) rb] + fcopy $rt $zf + close $rt + } elseif {$opts(-zipkit)} { + #TODO - update to zipfs ? + #see modpod + set zkd "#!/usr/bin/env tclkit\n\# This is a zip-based Tcl Module\n" + append zkd "package require vfs::zip\n" + append zkd "vfs::zip::Mount \[info script\] \[info script\]\n" + append zkd "if {\[file exists \[file join \[info script\] main.tcl\]\]} {\n" + append zkd " source \[file join \[info script\] main.tcl\]\n" + append zkd "}\n" + append zkd \x1A + puts -nonewline $zf $zkd + } + + #todo - subtract this from the endrec offset + if {$opts(-offsettype) eq "archive"} { + set dataStartOffset [tell $zf] ;#the overall file offset of the start of archive-data //JMN 2024 + } else { + set dataStartOffset 0 ;#offsets relative to file - the (old) zipfs mkzip way :/ + } + + set count 0 + set cd "" + + set members [list] + foreach path $paths { + #puts $path + lappend members $path + append cd [Addentry $zf $base $path $dataStartOffset] ;#path already includes relpath + incr count + } + set cdoffset [tell $zf] + set endrec [binary format a4ssssiis PK\05\06 0 0 \ + $count $count [string length $cd] [expr {$cdoffset - $dataStartOffset}]\ + [string length $opts(-comment)]] + append endrec $opts(-comment) + puts -nonewline $zf $cd + puts -nonewline $zf $endrec + close $zf + + set result "" + switch -exact -- $opts(-return) { + list { + set result $members + } + pretty { + if {[info commands showlist] ne ""} { + set result [plist -channel none members] + } else { + set result $members + } + } + none { + set result "" + } + } + return $result + } + + + #*** !doctools + #[list_end] [comment {--- end definitions namespace punk::zip ---}] +} +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# Secondary API namespace +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +tcl::namespace::eval punk::zip::lib { + tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase + tcl::namespace::path [tcl::namespace::parent] + #*** !doctools + #[subsection {Namespace punk::zip::lib}] + #[para] Secondary functions that are part of the API + #[list_begin definitions] + + #proc utility1 {p1 args} { + # #*** !doctools + # #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]] + # #[para]Description of utility1 + # return 1 + #} + + + + #*** !doctools + #[list_end] [comment {--- end definitions namespace punk::zip::lib ---}] +} +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +## Ready +package provide punk::zip [tcl::namespace::eval punk::zip { + variable pkg punk::zip + variable version + set version 0.2.0 +}] +return + +#*** !doctools +#[manpage_end] +