From 9353ed178cdef5c1e09b71efc908d98592488b3b Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Fri, 29 Aug 2025 16:11:23 +1000 Subject: [PATCH] update bootsupport and add bin/runtime.cmd script, plus linux fixes --- bin/runtime.cmd | 1171 +++++++++++++++++ src/bootsupport/lib/base64/ascii85.tcl | 13 +- src/bootsupport/lib/base64/base64.tcl | 9 +- src/bootsupport/lib/base64/base64c.tcl | 2 +- src/bootsupport/lib/base64/pkgIndex.tcl | 10 +- src/bootsupport/lib/base64/uuencode.tcl | 36 +- src/bootsupport/lib/base64/yencode.tcl | 26 +- src/bootsupport/lib/virtchannel_base/cat.tcl | 135 ++ .../lib/virtchannel_base/facade.tcl | 234 ++++ src/bootsupport/lib/virtchannel_base/fifo.tcl | 138 ++ .../lib/virtchannel_base/fifo2.tcl | 113 ++ .../lib/virtchannel_base/halfpipe.tcl | 194 +++ .../lib/virtchannel_base/memchan.tcl | 173 +++ src/bootsupport/lib/virtchannel_base/null.tcl | 54 + .../lib/virtchannel_base/nullzero.tcl | 62 + .../lib/virtchannel_base/pkgIndex.tcl | 17 + .../lib/virtchannel_base/random.tcl | 80 ++ .../lib/virtchannel_base/randseed.tcl | 58 + src/bootsupport/lib/virtchannel_base/std.tcl | 97 ++ .../lib/virtchannel_base/string.tcl | 126 ++ .../lib/virtchannel_base/textwindow.tcl | 74 ++ .../lib/virtchannel_base/variable.tcl | 181 +++ src/bootsupport/lib/virtchannel_base/zero.tcl | 54 + src/bootsupport/lib/virtchannel_core/core.tcl | 75 ++ .../lib/virtchannel_core/events.tcl | 156 +++ .../lib/virtchannel_core/pkgIndex.tcl | 8 + .../lib/virtchannel_core/transformcore.tcl | 71 + .../lib/virtchannel_transform/adler32.tcl | 103 ++ .../lib/virtchannel_transform/base64.tcl | 111 ++ .../lib/virtchannel_transform/counter.tcl | 94 ++ .../lib/virtchannel_transform/crc32.tcl | 103 ++ .../lib/virtchannel_transform/hex.tcl | 58 + .../lib/virtchannel_transform/identity.tcl | 59 + .../lib/virtchannel_transform/limitsize.tcl | 88 ++ .../lib/virtchannel_transform/observe.tcl | 80 ++ .../lib/virtchannel_transform/otp.tcl | 98 ++ .../lib/virtchannel_transform/pkgIndex.tcl | 14 + .../lib/virtchannel_transform/rot.tcl | 95 ++ .../lib/virtchannel_transform/spacer.tcl | 151 +++ .../lib/virtchannel_transform/zlib.tcl | 100 ++ .../modules/include_modules.config | 1 + src/bootsupport/modules/zzzload-0.1.0.tm | 131 ++ .../utility/scriptappwrappers/multishell.cmd | 15 +- src/runtime/mapvfs.config | 3 +- src/scriptapps/runtime.bash | 111 ++ src/scriptapps/runtime.ps1 | 184 +++ src/scriptapps/runtime_wrap.toml | 20 + .../thread3.0.2/libtcl9thread3.0.2.so | Bin 0 -> 90483 bytes .../linux-x86_64/thread3.0.2/pkgIndex.tcl | 55 + .../thread3.0.2/libtcl9thread3.0.2.so | Bin 0 -> 90483 bytes .../thread3.0.2/thread3.0.2/pkgIndex.tcl | 55 + 51 files changed, 5045 insertions(+), 51 deletions(-) create mode 100644 bin/runtime.cmd create mode 100644 src/bootsupport/lib/virtchannel_base/cat.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/facade.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/fifo.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/fifo2.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/halfpipe.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/memchan.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/null.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/nullzero.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/pkgIndex.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/random.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/randseed.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/std.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/string.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/textwindow.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/variable.tcl create mode 100644 src/bootsupport/lib/virtchannel_base/zero.tcl create mode 100644 src/bootsupport/lib/virtchannel_core/core.tcl create mode 100644 src/bootsupport/lib/virtchannel_core/events.tcl create mode 100644 src/bootsupport/lib/virtchannel_core/pkgIndex.tcl create mode 100644 src/bootsupport/lib/virtchannel_core/transformcore.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/adler32.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/base64.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/counter.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/crc32.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/hex.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/identity.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/limitsize.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/observe.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/otp.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/pkgIndex.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/rot.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/spacer.tcl create mode 100644 src/bootsupport/lib/virtchannel_transform/zlib.tcl create mode 100644 src/bootsupport/modules/zzzload-0.1.0.tm create mode 100644 src/scriptapps/runtime.bash create mode 100644 src/scriptapps/runtime.ps1 create mode 100644 src/scriptapps/runtime_wrap.toml create mode 100644 src/vendorlib_tcl9/linux-x86_64/thread3.0.2/libtcl9thread3.0.2.so create mode 100644 src/vendorlib_tcl9/linux-x86_64/thread3.0.2/pkgIndex.tcl create mode 100644 src/vendorlib_tcl9/linux-x86_64/thread3.0.2/thread3.0.2/libtcl9thread3.0.2.so create mode 100644 src/vendorlib_tcl9/linux-x86_64/thread3.0.2/thread3.0.2/pkgIndex.tcl diff --git a/bin/runtime.cmd b/bin/runtime.cmd new file mode 100644 index 00000000..6b1a49d3 --- /dev/null +++ b/bin/runtime.cmd @@ -0,0 +1,1171 @@ +: "punk MULTISHELL - shebangless polyglot for Tcl Perl sh bash cmd pwsh powershell" + "[rename set S;proc Hide x {proc $x args {}};Hide :]" + "\$(function : {<#pwsh#>})" + "perlhide" + qw^ +set -- "$@" "a=[Hide <#;Hide set;S 1 list]"; set -- : "$@";$1 = @' +: heredoc1 - hide from powershell using @ and squote above. close sqote for unix shells + ' \ +: .bat/.cmd launch section, leading colon hides from cmd, trailing slash hides next line from tcl + \ +: "[Hide @GOTO; Hide =begin; Hide @REM] #not necessary but can help avoid errs in testing" + +: << 'HEREDOC1B_HIDE_FROM_BASH_AND_SH' +: STRONG SUGGESTION: DO NOT MODIFY FIRST LINE OF THIS SCRIPT - except for first double quoted section. +: shebang line is not required on unix or windows and will reduce functionality and/or portability. +: Even comment lines can be part of the functionality of this script (both on unix and windows) - modify with care. +@GOTO :skip_perl_pod_start ^; +=begin excludeperl +: skip_perl_pod_start +: Continuation char at end of this line and rem with curly-braces used to exlude Tcl from the whole cmd block \ +: { +@REM ############################################################################################################################ +@REM THIS IS A POLYGLOT SCRIPT - supporting payloads in Tcl, bash, (some sh) and/or powershelll (powershell.exe or pwsh.exe) +@REM It should remain portable between unix-like OSes & windows if the proper structure is maintained. +@REM ############################################################################################################################ +@REM Change the value of nextshell to one of the supported types, and add code within payload sections for tcl,sh,bash,powershell as appropriate. +@REM This wrapper can be edited manually (carefully!) - or bash,tcl,perl,powershell scripts can be wrapped using the Tcl-based punkshell system +@REM e.g from within a running punkshell: dev scriptwrap.multishell -outputfolder +@REM Call with sh, bash, perl, or tclsh. (powershell untested on unix) +@REM Due to lack of shebang (#! line) Unix-like systems will hopefully default to a flavour of sh that can divert to bash if the script is called without an interpreter - but it may depend on the shell in use when called. +@REM If you find yourself really wanting/needing to add a shebang line - do so on the basis that the script will exist on unix-like systems only. +@REM in batch scripts - array syntax with square brackets is a simulation of arrays or associative arrays. +@REM note that many shells linked as sh do not support substition syntax and may fail - e.g dash etc - generally bash should be used in this context +@SETLOCAL EnableExtensions EnableDelayedExpansion +@SET "validshelltypes= pwsh____________ powershell______ sh______________ wslbash_________ bash____________ tcl_____________ perl____________ none____________" +@REM for batch - only win32 is relevant - but other scripts on other platforms also parse the nextshell block to determine next shell to launch +@REM nextshellpath and nextshelltype indices (underscore-padded to 16wide) are "other" plus those returned by Tcl platform pkg e.g win32,linux,freebsd,macosx +@REM The horrible underscore-padded fixed-widths are to keep the batch labels aligned whilst allowing values to be set +@REM If more than 64 chars needed for a target, it can still be done but overall script padding may need checking/adjusting +@REM Supporting more explicit oses than those listed may also require script padding adjustment +: <> +@SET "nextshellpath[win32___________]=powershell______________________________________________________" +@SET "nextshelltype[win32___________]=powershell______" +@SET "nextshellpath[dragonflybsd____]=/usr/bin/env bash_______________________________________________" +@SET "nextshelltype[dragonflybsd____]=bash____________" +@SET "nextshellpath[freebsd_________]=/usr/bin/env bash_______________________________________________" +@SET "nextshelltype[freebsd_________]=bash____________" +@SET "nextshellpath[netbsd__________]=/usr/bin/env bash_______________________________________________" +@SET "nextshelltype[netbsd__________]=bash____________" +@SET "nextshellpath[linux___________]=/usr/bin/env bash_______________________________________________" +@SET "nextshelltype[linux___________]=bash____________" +@SET "nextshellpath[macosx__________]=/usr/bin/env bash_______________________________________________" +@SET "nextshelltype[macosx__________]=bash____________" +@SET "nextshellpath[other___________]=/usr/bin/env bash_______________________________________________" +@SET "nextshelltype[other___________]=bash____________" +: <> +@rem asadmin is for automatic elevation to administrator. Separate window will be created (seems unavoidable with current elevation mechanism) and user will still get security prompt (probably reasonable). +: <> +@SET "asadmin=0" +: <> +@REM @ECHO nextshelltype is %nextshelltype[win32___________]% +@REM @SET "selected_shelltype=%nextshelltype[win32___________]%" +@SET "selected_shelltype=%nextshelltype[win32___________]%" +@REM @ECHO selected_shelltype %selected_shelltype% +@CALL :stringTrimTrailingUnderscores %selected_shelltype% selected_shelltype_trimmed +@REM @ECHO selected_shelltype_trimmed %selected_shelltype_trimmed% +@SET "selected_shellpath=%nextshellpath[win32___________]%" +@CALL :stringTrimTrailingUnderscores %selected_shellpath% selected_shellpath_trimmed +@CALL SET "keyRemoved=%%validshelltypes:!selected_shelltype!=%%" +@REM @ECHO keyremoved %keyRemoved% +@REM Note that 'powershell' e.g v5 is just a fallback for when pwsh is not available +@REM ## ### ### ### ### ### ### ### ### ### ### ### ### ### +@REM -- cmd/batch file section (ignored on unix but should be left in place) +@REM -- This section intended mainly to launch the next shell (and to escalate privileges if necessary) +@REM -- Avoid customising this if you are not familiar with batch scripting. cmd/batch script can be useful, but is probably the least expressive language and most error prone. +@REM -- For example - as this file needs to use unix-style lf line-endings - the label scanner is susceptible to the 512Byte boundary issue: https://www.dostips.com/forum/viewtopic.php?t=8988#p58888 +@REM -- This label issue can be triggered/abused in files with crlf line endings too - but it is less likely to happen accidentaly. +@REm -- See also: https://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/4095133#4095133 +@REM ############################################################################################################################ +@REM -- Due to this issue -seemingly trivial edits of the batch file section can break the script! (for Windows anyway) +@REM -- Even something as simple as adding or removing an @REM +@REM -- From within punkshell - use: +@REM -- deck scriptwrap.checkfile +@REM -- to check your templates or final wrapped scripts for byte boundary issues +@REM -- It will report any labels that are on boundaries +@REM -- This is why the nextshell value above is a 2 digit key instead of a string - so that editing the value doesn't change the byte offsets. +@REM -- Editing your sh,bash,tcl,pwsh payloads is much less likely to cause an issue. There is the possibility of the final batch :exit_multishell label spanning a boundary - so testing using deck scriptwrap.checkfile is still recommended. +@REM -- Alternatively, as you should do anyway - test the final script on windows +@REM -- Aside from adding comments/whitespace to tweak the location of labels - you can try duplicating the label (e.g just add the label on a line above) but this is not guaranteed to work in all situations. +@REM -- '@REM' is a safer comment mechanism than a leading colon - which is used sparingly here. +@REM -- A colon anywhere in the script that happens to land on a 512 Byte boundary (from file start or from a callsite) could be misinterpreted as a label +@REM -- It is unknown what versions of cmd interpreters behave this way - and deck scriptwrap.checkfile doesn't check all such boundaries. +@REm -- For this reason, batch labels should be chosen to be relatively unlikely to collide with other strings in the file, and simple names such as :exit or :end should probably be avoided +@REM ############################################################################################################################ +@REM -- custom windows payloads should be in powershell,tclsh (or sh/bash if available) code sections +@REM ## ### ### ### ### ### ### ### ### ### ### ### ### ### +@SET "winpath=%~dp0" +@SET "fname=%~nx0" +@REM @ECHO fname %fname% +@REM @ECHO winpath %winpath% +@REM @ECHO commandlineascalled %0 +@REM @ECHO commandlineresolved %~f0 +@CALL :getNormalizedScriptTail nftail +@REM @ECHO normalizedscripttail %nftail% +@CALL :getFileTail %0 clinetail +@REM @ECHO clinetail %clinetail% +@CALL :stringToUpper %~nx0 capscripttail +@REM @ECHO capscriptname: %capscripttail% + +@IF "%nftail%"=="%capscripttail%" ( + @ECHO forcing asadmin=1 due to file name on filesystem being uppercase + @SET "asadmin=1" +) else ( + @CALL :stringToUpper %clinetail% capcmdlinetail + @REM @ECHO capcmdlinetail !capcmdlinetail! + IF "%clinetail%"=="!capcmdlinetail!" ( + @ECHO forcing asadmin=1 due to cmdline scriptname in uppercase + @set "asadmin=1" + ) +) +@SET "vbsGetPrivileges=%temp%\punk_bat_elevate_%fname%.vbs" +@SET arglist=%* +@SET "qstrippedargs=args%arglist%" +@SET "qstrippedargs=%qstrippedargs:"=%" +@IF "is%qstrippedargs:~4,13%"=="isPUNK-ELEVATED" ( + GOTO :gotPrivileges +) +@IF !asadmin!==1 ( + net file 1>NUL 2>NUL + @IF '!errorlevel!'=='0' ( GOTO :gotPrivileges ) else ( GOTO :getPrivileges ) +) +@REM padding +@REM padding +@REM padding +@REM padding +@REM padding +@REM padding +@REM padding +@REM padding +@REM padding +@REM padding +@REM padding +@REM padding +@GOTO skip_privileges +:getPrivileges +@IF "is%qstrippedargs:~4,13%"=="isPUNK-ELEVATED" (echo PUNK-ELEVATED & shift /1 & goto :gotPrivileges ) +@ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%" +@ECHO args = "PUNK-ELEVATED " >> "%vbsGetPrivileges%" +@ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%" +@ECHO args = args ^& strArg ^& " " >> "%vbsGetPrivileges%" +@ECHO Next >> "%vbsGetPrivileges%" +@ECHO UAC.ShellExecute "%~dp0%~n0%~x0", args, "", "runas", 1 >> "%vbsGetPrivileges%" +@ECHO Launching script in new window due to administrator elevation +@"%SystemRoot%\System32\WScript.exe" "%vbsGetPrivileges%" %* +@EXIT /B + +:gotPrivileges +@REM setlocal & pushd . +@PUSHD . +@cd /d %~dp0 +@IF "is%qstrippedargs:~4,13%"=="isPUNK-ELEVATED" ( + @DEL "%vbsGetPrivileges%" 1>nul 2>nul + @SET arglist=%arglist:~14% +) + +:skip_privileges +@SET need_ps1=0 +@REM we want the ps1 to exist even if the nextshell isn't powershell +@if not exist "%~dp0%~n0.ps1" ( + @SET need_ps1=1 +) ELSE ( + fc "%~dp0%~n0%~x0" "%~dp0%~n0.ps1" >nul || goto different + @REM @ECHO "files same" + @SET need_ps1=0 +) +@GOTO :pscontinue +:different +@REM @ECHO "files differ" +@SET need_ps1=1 +:pscontinue +@IF !need_ps1!==1 ( + COPY "%~dp0%~n0%~x0" "%~dp0%~n0.ps1" >NUL +) +@REM avoid using CALL to launch pwsh,tclsh etc - it will intercept some args such as /? +@IF "!selected_shelltype_trimmed!"=="none" ( + SET selected_shelltype_trimmed=pwsh +) +@IF "!selected_shelltype_trimmed!"=="pwsh" ( + REM pwsh vs powershell hasn't been tested because we didn't need to copy cmd to ps1 this time + REM test availability of preferred option of powershell7+ pwsh + pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted 2>NUL; write-host "statusmessage: pwsh-found" >NUL + SET pwshtest_exitcode=!errorlevel! + REM ECHO pwshtest_exitcode !pwshtest_exitcode! + REM fallback to powershell if pwsh failed + IF !pwshtest_exitcode!==0 ( + pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted; "%~dp0%~n0.ps1" %arglist% + SET task_exitcode=!errorlevel! + ) ELSE ( + REM TODO prompt user with option to call script to install pwsh using winget + REM powershell -nop -nol -c set-executionpolicy -Scope Process Unrestricted; "%~dp0%~n0.ps1" %arglist% + powershell -nop -nol -ExecutionPolicy Bypass -c "%~dp0%~n0.ps1" %arglist% + SET task_exitcode=!errorlevel! + ) +) ELSE ( + IF "!selected_shelltype_trimmed!"=="powershell" ( + powershell -nop -nol -ExecutionPolicy Bypass -c "%~dp0%~n0.ps1" %arglist% + SET task_exitcode=!errorlevel! + ) ELSE ( + IF "!selected_shelltype_trimmed!"=="wslbash" ( + CALL :getWslPath %winpath% wslpath + REM ECHO wslfullpath "!wslpath!%fname%" + %selected_shellpath_trimmed% "!wslpath!%fname%" %arglist% + SET task_exitcode=!errorlevel! + ) ELSE ( + REM perl or tcl or sh or bash + IF NOT "x%keyRemoved%"=="x%validshelltypes%" ( + REM sh on windows uses /c/ instead of /mnt/c - at least if using msys. Todo, review what is the norm on windows with and without msys2,cygwin,wsl + REM and what logic if any may be needed. For now sh with /c/xxx seems to work the same as sh with c:/xxx + REM The compound statement with trailing call is required to stop batch termination confirmation, whilst still capturing exitcode + @ECHO HERE "!selected_shelltype_trimmed!" "!selected_shellpath_trimmed!" + %selected_shellpath_trimmed% "%~dp0%fname%" %arglist% & SET task_exitcode=!errorlevel! & Call; + ) ELSE ( + ECHO %fname% has invalid nextshelltype value %selected_shelltype% valid options are %validshelltypes% + SET task_exitcode=66 + @REM boundary padding + GOTO :exit_multishell + ) + ) + ) +) +@REM batch file library functions +@REM boundary padding +@GOTO :endlib + +:getWslPath +@SETLOCAL + @SET "_path=%~p1" + @SET "name=%~nx1" + @SET "drive=%~d1" + @SET "rtrn=%~2" + @REM Although drive letters on windows are normally upper case wslbash seems to expect lower case drive letters + @CALL :stringToLower %drive ldrive + @SET "result=/mnt/%ldrive:~0,1%%_path:\=/%%name%" +@ENDLOCAL & ( + @if "%~2" neq "" ( + SET "%rtrn%=%result%" + ) ELSE ( + ECHO %result% + ) +) +@EXIT /B + +:getFileTail +@REM return tail of file without any normalization e.g c:/punkshell/bin/Punk.cmd returns Punk.cmd even if file is punk.cmd +@REM we can't use things such as %~nx1 as it can change capitalisation +@REM This function is designed explicitly to preserve capitalisation +@REM accepts full paths with either / or \ as delimiters - or +@SETLOCAL + @SET "rtrn=%~2" + @SET "arg=%~1" + @REM @SET "result=%_arg:*/=%" + @REM @SET "result=%~1" + @SET LF=^ + + + : The above 2 empty lines are important. Don't remove + @CALL :stringContains "!arg!" "\" hasBackSlash + @IF "!hasBackslash!"=="true" ( + @for %%A in ("!LF!") do @( + @FOR /F %%B in ("!arg:\=%%~A!") do @set "result=%%B" + ) + ) ELSE ( + @CALL :stringContains "!arg!" "/" hasForwardSlash + @IF "!hasForwardSlash!"=="true" ( + @FOR %%A in ("!LF!") do @( + @FOR /F %%B in ("!arg:/=%%~A!") do @set "result=%%B" + ) + ) ELSE ( + @set "result=%arg%" + ) + ) +@ENDLOCAL & ( + @if "%~2" neq "" ( + @SET "%rtrn%=%result%" + ) ELSE ( + @ECHO %result% + ) +) +@EXIT /B +@REM boundary padding +@REM boundary padding +:getNormalizedScriptTail +@SETLOCAL + @SET "result=%~nx0" + @SET "rtrn=%~1" +@ENDLOCAL & ( + @IF "%~1" neq "" ( + @SET "%rtrn%=%result%" + ) ELSE ( + @ECHO %result% + ) +) +@EXIT /B + +:getNormalizedFileTailFromPath +@REM warn via echo, and do not set return variable if path not found +@REM note that %~nx1 does not preserve case of provided path - hence the name 'normalized' +@REM boundary padding +@REM boundary padding +@REM boundary padding +@REM boundary padding +@SETLOCAL + @CALL :stringContains %~1 "\" hasBackSlash + @CALL :stringContains %~1 "/" hasForwardSlash + @IF "%hasBackslash%-%hasForwardslash%"=="false-false" ( + @SET "P=%cd%%~1" + @CALL :getNormalizedFileTailFromPath "!P!" ftail2 + @SET "result=!ftail2!" + ) else ( + @IF EXIST "%~1" ( + @SET "result=%~nx1" + ) else ( + @ECHO error getNormalizedFileTailFromPath file not found: %~1 + @EXIT /B 1 + ) + ) + @SET "rtrn=%~2" +@ENDLOCAL & ( + @IF "%~2" neq "" ( + SET "%rtrn%=%result%" + ) ELSE ( + @ECHO getNormalizedFileTailFromPath %1 result: %result% + ) +) +@EXIT /B + +:stringContains +@REM usage: @CALL:stringContains string needle returnvarname +@SETLOCAL + @SET "rtrn=%~3" + @SET "string=%~1" + @SET "needle=%~2" + @IF "!string:%needle%=!"=="!string!" @( + @SET "result=false" + ) ELSE ( + @SET "result=true" + ) +@ENDLOCAL & ( + @IF "%~3" neq "" ( + @SET "%rtrn%=%result%" + ) ELSE ( + @ECHO stringContains %string% %needle% result: %result% + ) +) +@EXIT /B +@REM boundary padding +@REM boundary padding +:stringToUpper +@SETLOCAL + @SET "rtrn=%~2" + @SET "string=%~1" + @SET "capstring=%~1" + @FOR %%A in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO @( + @SET "capstring=!capstring:%%A=%%A!" + ) + @SET "result=!capstring!" +@ENDLOCAL & ( + @IF "%~2" neq "" ( + @SET "%rtrn%=%result%" + ) ELSE ( + @ECHO stringToUpper %string% result: %result% + ) +) +@EXIT /B +:stringToLower +@SETLOCAL + @SET "rtrn=%~2" + @SET "string=%~1" + @SET "retstring=%~1" + @FOR %%A in (a b c d e f g h i j k l m n o p q r s t u v w x y z) DO @( + @SET "retstring=!retstring:%%A=%%A!" + ) + @SET "result=!retstring!" +@ENDLOCAL & ( + @IF "%~2" neq "" ( + @SET "%rtrn%=%result%" + ) ELSE ( + @ECHO stringToLower %string% result: %result% + ) +) +@EXIT /B +@REM boundary padding +@REM boundary padding +:stringTrimTrailingUnderscores +@SETLOCAL + @SET "rtrn=%~2" + @SET "string=%~1" + @SET "trimstring=%~1" + @REM trim up to 63 underscores from the end of a string using string substitution + @SET "trimstring=%trimstring%###" + @SET "trimstring=%trimstring:________________________________###=###%" + @SET "trimstring=%trimstring:________________###=###%" + @SET "trimstring=%trimstring:________###=###%" + @SET "trimstring=%trimstring:____###=###%" + @SET "trimstring=%trimstring:__###=###%" + @SET "trimstring=%trimstring:_###=###%" + @SET "trimstring=%trimstring:###=%" + @SET "result=!trimstring!" +@ENDLOCAL & ( + @IF "%~2" neq "" ( + @SET "%rtrn%=%result%" + ) ELSE ( + @ECHO stringTrimTrailingUnderscores %string% result: %result% + ) +) +@EXIT /B +:isNumeric +@SETLOCAL + @SET "notnumeric="&FOR /F "delims=0123456789" %%i in ("%1") do set "notnumeric=%%i" + @IF defined notnumeric ( + @SET "result=false" + ) else ( + @SET "result=true" + ) + @SET "rtrn=%~2" +@ENDLOCAL & ( + @IF "%~2" neq "" ( + @SET "%rtrn%=%result%" + ) ELSE ( + @ECHO %result% + ) +) +@EXIT /B + +:endlib +: \ +@REM padding +@REM padding +@REM @SET taskexit_code=!errorlevel! & goto :exit_multishell +@GOTO :exit_multishell +# } +# -*- tcl -*- +# ## ### ### ### ### ### ### ### ### ### ### ### ### ### +# -- tcl script section +# -- This is a punk multishell file +# -- Primary payload target is Tcl, with sh,bash,powershell as helpers +# -- but it may equally be used with any of these being the primary script. +# -- It is tuned to run when called as a batch file, a tcl script a sh/bash script or a pwsh/powershell script +# -- i.e it is a polyglot file. +# -- The specific layout including some lines that appear just as comments is quite sensitive to change. +# -- It can be called on unix or windows platforms with or without the interpreter being specified on the commandline. +# -- e.g ./filename.polypunk.cmd in sh or bash +# -- e.g tclsh filename.cmd +# -- +# ## ### ### ### ### ### ### ### ### ### ### ### ### ### +rename set ""; rename S set; set k {-- "$@" "a}; if {[info exists ::env($k)]} {unset ::env($k)} ;# tidyup and restore +Hide :exit_multishell;Hide {<#};Hide '@ +namespace eval ::punk::multishell { + set last_script_root [file dirname [file normalize ${::argv0}/__]] + set last_script [file dirname [file normalize [info script]/__]] + if {[info exists ::argv0] && + $last_script eq $last_script_root + } { + set ::punk::multishell::is_main($last_script) 1 ;#run as executable/script - likely desirable to launch application and return an exitcode + } else { + set ::punk::multishell::is_main($last_script) 0 ;#sourced - likely to be being used as a library - no launch, no exit. Can use return. + } + if {"::punk::multishell::is_main" ni [info commands ::punk::multishell::is_main]} { + proc ::punk::multishell::is_main {{script_name {}}} { + if {$script_name eq ""} { + set script_name [file dirname [file normalize [info script]/--]] + } + if {![info exists ::punk::multishell::is_main($script_name)]} { + #e.g a .dll or something else unanticipated + puts stderr "Warning punk::multishell didn't recognize info script result: $script_name - will treat as if sourced and return instead of exiting" + puts stderr "Info: script_root: [file dirname [file normalize ${::argv0}/__]]" + return 0 + } + return [set ::punk::multishell::is_main($script_name)] + } + } +} +# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin Tcl Payload +#puts "script : [info script]" +#puts "argcount : $::argc" +#puts "argvalues: $::argv" +#puts "argv0 : $::argv0" +# -- --- --- --- --- --- --- --- --- --- --- --- + +# +puts stderr "No tcl code for this script. Try another program such as perl or bash" +# + +# +# + +# +# + + +# +# + + +# -- --- --- --- --- --- --- --- --- --- --- --- +# -- Best practice is to always return or exit above, or just by leaving the below defaults in place. +# -- If the multishell script is modified to have Tcl below the Tcl Payload section, +# -- then Tcl bracket balancing needs to be carefully managed in the shell and powershell sections below. +# -- Only the # in front of the two relevant if statements below needs to be removed to enable Tcl below +# -- but the sh/bash 'then' and 'fi' would also need to be uncommented. +# -- This facility left in place for experiments on whether configuration payloads etc can be appended +# -- to tail of file - possibly binary with ctrl-z char - but utility is dependent on which other interpreters/shells +# -- can be made to ignore/cope with such data. +if {[::punk::multishell::is_main]} { + exit 0 +} else { + return +} +# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end Tcl Payload +# end hide from unix shells \ +HEREDOC1B_HIDE_FROM_BASH_AND_SH +# csh/tcsh/sh/bash use oldschool backticks and sed lowest common denominator \ +echo "script: `echo $0 | sed 's/^-//'`" +# csh/tcsh/sh/bash use oldschool backticks and sed lowest common denominator \ +echo "shell: " `ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()' | sed -E 's/^.*\/|^-//'` +#csh/tcsh diversion \ +test "$argv[*]" != "[*]" && ( /usr/bin/env bash $argv[*]; exit ) +#other non-bash diversion \ +test `ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()' | sed -E 's/^.*\/|^-//'` != "bash" && /usr/bin/env bash $0 +#review \ +test `ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()' | sed -E 's/^.*\/|^-//'` != "bash" && exit +# sh/bash \ +shift && set -- "${@:1:$#-1}" + +#echo "shell:" `ps -o args= $$ | sed -E 's/^.*\/|^-//' | awk '{print $1}'` +#------------------------------------------------------ +# -- This if block only needed if Tcl didn't exit or return above. +if false==false # else { + then + : # +# ## ### ### ### ### ### ### ### ### ### ### ### ### ### +# -- sh/bash script section +# -- leave as is if all that is required is launching the Tcl payload" +# -- +# -- Note that sh/bash script isn't called when running a .bat/.cmd from cmd.exe on windows by default +# -- adjust the %nextshell% value above +# -- if sh/bash scripting needs to run on windows too. +# -- +# ## ### ### ### ### ### ### ### ### ### ### ### ### ### + +if [[ "$OSTYPE" == "linux"* ]]; then + os="linux" +elif [[ "$OSTYPE" == "darwin"* ]]; then + os="macosx" +elif [[ "$OSTYPE" == "freebsd"* ]]; then + os="freebsd" +elif [[ "$OSTYPE" == "dragonflybsd"* ]]; then + os="dragonflybsd" +elif [[ "$OSTYPE" == "netbsd"* ]]; then + os="netbsd" +elif [[ "$OSTYPE" == "win32" ]]; then + os="win32" +elif [[ "$OSTYPE" == "msys" ]]; then + echo MSYS + os="win32" + #review - need ps/sed/awk to determine shell? + interp = `ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()' | sed -E 's/^.*\/|^-//'` + #use 'command -v' (shell builtin preferred over external which) + shellpath=`command -v $interp` + shellfolder="${shellpath%/*}" #avoid dependency on basename or dirname + #"c:/windows/system32/" is quite likely in the path ahead of msys,git etc. + #This breaks calls to various unix utils such as sed etc (wsl related?) + export PATH="$shellfolder${PATH:+:${PATH}}" +else + #os="$OSTYPE" + os="other" +fi +echo ostype: $OSTYPE +shellconfigline=$( sed -n "/: <>/{:a;n;/: <>/q;p;ba}" "$0" | grep $os) +#echo $shellconfigline; +if [[ $shellconfigline == *"nextshelltype"* ]]; then + echo "found config for os $os" + split1="${shellconfigline#*=}" #remove everything through the first '=' + #echo "split1: $split1" + pathraw="${split1%%\"*}" #take everything before the quote - use %% to get longest match + pathraw="${pathraw//\"/}" #remove quote + nextshellpath="${pathraw/%_*/}" #remove trailing underscores (% = must match at end) + #echo "nextshellpath: $nextshellpath" + split2="${split1#*=}" + #echo "split2: $split2" + split2="${split2//\"/}" + nextshelltype="${split2/%_*/}" + echo "nextshelltype: $nextshelltype" +else + echo "unable to find config for os $os" + echo "shellconfigline: $shellconfigline" + nextshellpath="" + nextshelltype="" +fi +exitcode=0 +#-- sh/bash launches nextscript here instead of shebang line at top +if [[ "$nextshelltype" != "bash" && "$nextshelltype" != "none" ]]; then + #echo bash launching subshell of type $nextshelltype $nextshellpath on "$0" + #/usr/bin/env tclsh "$0" "$@" + ${nextshellpath} "$0" "$@" + + exitcode=$? + #echo "sh/bash reporting exitcode: ${exitcode}" + exit $exitcode + #-- override exitcode example + #exit 66 +else + #already in bash - don't launch another process or we would loop + #echo "bash payload" + : +fi +# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin sh Payload +#printf "start of bash or sh code" + +# + +wdir="$(pwd)"; [ "$(pwd)" = "/" ] && wdir="" +case "$0" in + /*) scriptpath="${0}";; + *) scriptpath="$wdir/${0#./}";; +esac +scriptdir="${scriptpath%/*}" +scriptdir=$(realpath $scriptdir) +scriptpath=$(realpath $scriptpath) +basename=$(basename "$scriptpath") #e.g fetchruntime.bash +scriptroot="${basename%.*}" #e.g "fetchruntime" + +url_kitbase="https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" +runtime_available=0 +if [[ "$OSTYPE" == "linux"* ]]; then + arch=$(uname -i) + if [[ "$arch" == "x86_64"* ]]; then + url="${url_kitbase}/linux-x86_64/tclkit-902-Linux64-intel-dyn" + archdir="${scriptdir}/runtime/linux-x86_64" + output="${archdir}/tclkit-902-Linux64-intel-dyn" + runtime_available=1 + elif [[ "$arch" == "arm"* ]]; then + url="${url_kitbase}/linux-arm/tclkit-902-Linux64-arm-dyn" + archdir="${scriptdir}/runtime/linux-arm" + output="${archdir}/tclkit-902-Linux64-arm-dyn" + runtime_available=1 + fi + if [[ "$runtime_available" -eq 1 ]]; then + echo "Please ensure libxFt.so.2 is available" + echo "e.g on Ubuntu: sudo apt-get install libxft2" + fi + os="linux" +elif [[ "$OSTYPE" == "darwin"* ]]; then + os="macosx" + #assumed to be Mach-O 'universal binaries' for both x86-64 and arm? - REVIEW + url="${url_kitbase}/macosx/tclkit-902-Darwin64-dyn" + archdir="${scriptdir}/runtime/macosx/" + output="${archdir}/tclkit-902-Darwin64-dyn" + runtime_available=1 +elif [[ "$OSTYPE" == "freebsd"* ]]; then + os="freebsd" +elif [[ "$OSTYPE" == "dragonflybsd"* ]]; then + os="dragonflybsd" +elif [[ "$OSTYPE" == "netbsd"* ]]; then + os="netbsd" +elif [[ "$OSTYPE" == "win32" ]]; then + os="win32" + url="${url_kitbase}/win32-x86_64/tclsh902z.exe" + archdir="${scriptdir}/runtime/win32-x86_64/" + output="${archdir}/tcsh902z.exe" + runtime_available=1 +elif [[ "$OSTYPE" == "msys" ]]; then + echo MSYS + os="win32" + #use 'command -v' (shell builtin preferred over external which) + interp = `ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()' | sed -E 's/^.*\/|^-//'` + shellpath=`command -v $interp` + shellfolder="${shellpath%/*}" #avoid dependency on basename or dirname + #"c:/windows/system32/" is quite likely in the path ahead of msys,git etc. + #This breaks calls to various unix utils such as sed etc (wsl related?) + export PATH="$shellfolder${PATH:+:${PATH}}" + url="${url_kitbase}/win32-x86_64/tclsh902z.exe" + archdir="${scriptdir}/runtime/win32-x86_64" + output="${archdir}/tclsh902z.exe" + runtime_available=1 +else + #os="$OSTYPE" + os="other" +fi + +case "$1" in + "fetch") + + if [[ "$runtime_available" -eq 1 ]]; then + #test win32 + mkdir -p $archdir + echo "Attempting to download $url" + #wget $url -O $output + curl -SL --output "$output" "$url" + if [[ $? -eq 0 ]]; then + echo "File downloaded to $output" + chmod +x $output + else + echo "Error: Failed to download to $output" + fi + else + echo "No runtime currently available for $os" + fi + ;; + "list") + if [ -d $archdir ]; then + echo "$(ls $archdir -1 | wc -l) files in $archdir" + echo $(ls $archdir -1) + else + echo "No runtimes available in $archdir\n Use '$0 fetch' to install." + fi + ;; + "run") + #todo - lookup active runtime for os-arch from .toml file + activeruntime=$(ls $archdir -1 | tail -n 1) + activeruntime_fullpath="$archdir/$activeruntime" + echo "using $activeruntime_fullpath" + shift + echo "args: $@" + $activeruntime_fullpath "$@" + ;; + *) + echo "Usage: $0 {fetch|list|run}" + exit 1 + ;; +esac + +# + +# +# + +# -- --- --- --- --- --- --- --- +# +#-- sh/bash launches Tcl here instead of shebang line at top +#-- use exec to use exitcode (if any) directly from the tcl script +#exec /usr/bin/env tclsh "$0" "$@" +#-- alternative - can run sh/bash script after the tcl call. +#/usr/bin/env tclsh "$0" "$@" +#exitcode=$? +#echo "sh/bash reporting tcl exitcode: ${exitcode}" +#-- override exitcode example +#exit 66 +# +# -- --- --- --- --- --- --- --- + +# +# + + +#printf "sh/bash done \n" +# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end sh Payload +#------------------------------------------------------ +fi +exit ${exitcode} +# ## ### ### ### ### ### ### ### ### ### ### ### ### ### +# -- Perl script section +# -- leave the script below as is, if all that is required is launching the Tcl payload" +# -- +# -- Note that perl script isn't called by default when simply running this script by name +# -- adjust the nextshell value at the top of the script to point to perl +# -- +# ## ### ### ### ### ### ### ### ### ### ### ### ### ### +=cut +#!/user/bin/perl +my $exit_code = 0; +use Cwd qw(abs_path); +my $scriptname = abs_path($0); +#print "perl $scriptname\n"; +my $os = "$^O"; +if ($os eq "MSWin32") { + $os = "win32"; +} elsif ($os eq "darwin") { + $os = "macosx"; +} +print "os $os\n"; +# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin perl Payload +#use ExtUtils::Installed; +#my $installed = ExtUtils::Installed->new(); +#my @modules = $installed->modules(); +#print "Modules:\n"; +#foreach my $m (@modules) { +# print "$m\n"; +#} +# -- --- --- + + + +my $i =1; +foreach my $a(@ARGV) { + print "Arg # $i: $a\n"; +} + +# +print STDERR "No perl code for this script. Try another program such as tcl or bash"; +# + +# +# + + + +# -- --- --- --- --- --- --- --- +# +#$exit_code=system("tclsh", $scriptname, @ARGV); +#print "perl reporting tcl exitcode: $exit_code"; +# +# -- --- --- --- --- --- --- --- + +# +# + + +# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end perl Payload +exit $exit_code; +__END__ + +# end hide sh/bash/perl block from Tcl +# This comment with closing brace should stay in place whether if commented or not } +#------------------------------------------------------ +# begin hide powershell-block from Tcl - only needed if Tcl didn't exit or return above +if 0 { +: end heredoc1 - end hide from powershell \ +'@ +# ## ### ### ### ### ### ### ### ### ### ### ### ### ### +# -- powershell/pwsh section +# -- Do not edit if current file is the .ps1 +# -- Edit the corresponding .cmd and it will autocopy +# -- unbalanced braces { } here *even in comments* will cause problems if there was no Tcl exit or return above +# -- custom script should generally go below the begin_powershell_payload line +# ## ### ### ### ### ### ### ### ### ### ### ### ### ### +function GetScriptName { $myInvocation.ScriptName } +$scriptname = GetScriptName +function GetDynamicParamDictionary { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true, Mandatory=$true)] + [string] $CommandName + ) + + begin { + # Get a list of params that should be ignored (they're common to all advanced functions) + $CommonParameterNames = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject([type] [System.Management.Automation.Internal.CommonParameters]) | + Get-Member -MemberType Properties | + Select-Object -ExpandProperty Name + } + + process { + # Create the dictionary that this scriptblock will return: + $DynParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary + + # Convert to object array and get rid of Common params: + (Get-Command $CommandName | select -exp Parameters).GetEnumerator() | + Where-Object { $CommonParameterNames -notcontains $_.Key } | + ForEach-Object { + $DynamicParameter = New-Object System.Management.Automation.RuntimeDefinedParameter ( + $_.Key, + $_.Value.ParameterType, + $_.Value.Attributes + ) + $DynParamDictionary.Add($_.Key, $DynamicParameter) + } + + # Return the dynamic parameters + return $DynParamDictionary + } +} +# GetDynamicParamDictionary +# - This can make it easier to share a single set of param definitions between functions +# - sample usage +#function ParameterDefinitions { +# param( +# [Parameter(Mandatory)][string] $myargument +# ) +#} +#function psmain { +# [CmdletBinding()] +# param() +# dynamicparam { GetDynamicParamDictionary ParameterDefinitions } +# process { +# #called once with $PSBoundParameters dictionary +# #can be used to validate arguments, or set a simpler variable name for access +# switch ($PSBoundParameters.keys) { +# 'myargumentname' { +# Set-Variable -Name $_ -Value $PSBoundParameters."$_" +# } +# #... +# } +# foreach ($boundparam in $PSBoundParameters.GetEnumerator()) { +# #... +# } +# } +# end { +# #Main function logic +# Write-Host "myargumentname value is: $myargumentname" +# #myotherfunction @PSBoundParameters +# } +#} +#psmain @args +#"Timestamp : {0,10:yyyy-MM-dd HH:mm:ss}" -f $(Get-Date) | write-host +#"Script Name : {0}" -f $scriptname | write-host +#"Powershell Version: {0}" -f $PSVersionTable.PSVersion.Major | write-host +#"powershell args : {0}" -f ($args -join ", ") | write-host +# -- --- --- --- +$startTag = ": <>" +$endTag = ": <>" +$fileContent = Get-Content $scriptname -Raw +$pattern = "(?s)$startTag(.*?)$endTag" +$matches = [regex]::Matches($fileContent,$pattern) +$admininfo = $matches[0].Groups[1].Value +$asadmin = 0 +if ($matches.count) { + $asadmin = $admininfo.Contains("asadmin=1") + if ($asadmin) { + if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + # If not elevated, relaunch with elevated privileges + # -Wait e.g for starting a service or other operations which remainder of script may depend on + $arguments = @("-NoProfile", "-NoExit", "-ExecutionPolicy", "Bypass") + $arguments += @("-File", $($MyInvocation.MyCommand.Path)) + $arguments += $args + if ($PSVersionTable.PSEdition -eq 'Core') { + Start-Process -FilePath "pwsh.exe" -ArgumentList $arguments -Wait -Verb RunAs + } else { + Start-Process -FilePath "powershell.exe" -ArgumentList $arguments -Wait -Verb RunAs + } + Exit # Exit the current non-elevated process + } + } +} +# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin powershell Payload + +# + + +function GetDynamicParamDictionary { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true, Mandatory=$true)] + [string] $CommandName + ) + + begin { + # Get a list of params that should be ignored (they're common to all advanced functions) + $CommonParameterNames = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject([type] [System.Management.Automation.Internal.CommonParameters]) | + Get-Member -MemberType Properties | + Select-Object -ExpandProperty Name + } + + process { + # Create the dictionary that this scriptblock will return: + $DynParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary + + # Convert to object array and get rid of Common params: + (Get-Command $CommandName | Select-Object -exp Parameters).GetEnumerator() | + Where-Object { $CommonParameterNames -notcontains $_.Key } | + ForEach-Object { + $DynamicParameter = New-Object System.Management.Automation.RuntimeDefinedParameter ( + $_.Key, + $_.Value.ParameterType, + $_.Value.Attributes + ) + $DynParamDictionary.Add($_.Key, $DynamicParameter) + } + + # Return the dynamic parameters + return $DynParamDictionary + } +} +function ParameterDefinitions { + param( + [Parameter(ValueFromRemainingArguments=$true)] $opts + ) +} + +function psmain { + [CmdletBinding()] + #Empty param block (extra params can be added) + param( + [Parameter(Mandatory=$false)][string] $action + ) + dynamicparam { + if ($action -eq 'list') { + } elseif ($action -eq 'fetch') { + #GetDynamicParamDictionary ParameterDefinitions + $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "fetchruntime" + Mandatory = $false + } + $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $attributeCollection.Add($parameterAttribute) + + $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'runtime', [string], $attributeCollection + ) + + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() + $paramDictionary.Add('runtime', $dynParam1) + return $paramDictionary + } elseif ($action -eq 'run') { + GetDynamicParamDictionary ParameterDefinitions + } else { + } + } + process { + #Called once - we get a single item being our PSBoundParameters dictionary + #write-host "Bound Parameters:$($PSBoundParameters.Keys)" + switch ($PSBoundParameters.keys) { + 'action' { + #write-host "got action " $PSBoundParameters.action + Set-Variable -Name $_ -Value $PSBoundParameters."$_" + $known_actions = @("fetch", "list", "run") + if (-not($known_actions -contains $action)) { + write-host "fetch '$action' not understood. Known_actions: $known_actions" + exit 1 + } + } + 'opts' { + #write-warning "Unused parameters: $($PSBoundParameters.$_)" + } + Default { + #write-warning "Unhandled parameter -> [$($_)]" + } + } + #foreach ($boundparam in $PSBoundParameters.Keys) { + # write-host "k: $boundparam" + #} + } + end { + # PSBoundParameters + #write-host "action:'$action'" + $outbase = $PSScriptRoot + $outbase = Resolve-Path -Path $outbase + #expected script location is the bin folder of a punk project + $rtfolder = Join-Path -Path $outbase -ChildPath "runtime" + $archfolder = Join-Path -Path $rtfolder -ChildPath "win32-x86_64" + switch ($action) { + 'fetch' { + $runtime = "tclsh902z.exe" + $archurl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master/win32-x86_64" + foreach ($boundparam in $PSBoundParameters.Keys) { + write-host "fetchopt: $boundparam $($PSBoundParameters[$boundparam])" + } + if ( $PSBoundParameters["runtime"].Length ) { + $runtime = $PSBoundParameters["runtime"] + } + $fileurl = "$archurl/$runtime" + $output = join-path $archfolder $runtime + + $container = split-path -Path $output -Parent + new-item -Path $container -ItemType Directory -force #create with intermediary folders if not already present + + if (-not(Test-Path -Path $output -PathType Leaf)) { + Write-Host "Downloading from $fileurl ..." + try { + $response = Invoke-WebRequest -Uri $fileurl -OutFile $output -ErrorAction Stop + Write-Host "Runtime saved at $output" + } + catch { + Write-Host "An error occurred: $($_.Exception.Message)" + if ($_.Exception.Response) { + Write-Host "HTTP Status code: $($_.Exception.Response.StatusCode)" + } + } + } else { + Write-Host "Runtime already found at $output" + } + } + 'run' { + #select first (or configured default) runtime and launch, passing arguments + if (-not(Test-Path -Path $archfolder -PathType Container)) { + write-host "No runtimes seem to be installed for win32-x86_64`nPlease use 'runtime.cmd fetch' to install" + } else { + $dircontents = (get-childItem -Path $archfolder -File | Sort-Object Name) + if ($dircontents.Count -gt 0) { + #write-host "run.." + #write-host "num params: $($PSBoundParameters.opts.count)" + #foreach ($boundparam in $PSBoundParameters.opts) { + # write-host $boundparam + #} + #todo - use 'active' runtime - need to lookup (PSToml?) + #when no 'active' runtime for this os-arch - use last item (sorted in dictionary order) + $active = $dircontents[-1] + #write-host "using: $active" + Start-Process -FilePath $active -ArgumentList $PSBoundParameters.opts -NoNewWindow -Wait + } else { + write-host "No files found in $archfolder" + write-host "No runtimes seem to be installed for win32-x86_64`nPlease use 'runtime.cmd fetch' to install." + } + } + } + 'list' { + if (test-path -Path $archfolder -Type Container) { + $dircontents = (get-childItem -Path $archfolder -File) + write-host "$(${dircontents}.count) files in $archfolder" + foreach ($f in $dircontents) { + write-host $f.Name + } + } else { + write-host "No runtimes seem to be installed for win32-x86_64 in $archfolder`nPlase use 'runtime.cmd fetch' to install." + } + } + default { + $actions = @("fetch", "list", "run") + write-host "Available actions: $actions" + } + } + + return $PSBoundParameters + } +} +#write-host (psmain @args) +$returnvalue = psmain @args +#Write-Host "Function Returned $returnvalue" -ForegroundColor Cyan +return $returnvalue +exit 0 + + +# + +# +# + + +# -- --- --- --- --- --- --- --- +# +#tclsh $scriptname $args +#"powershell reporting exitcode: {0}" -f $LASTEXITCODE | write-host +# +# -- --- --- --- --- --- --- --- + + +# +# + +# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end powershell Payload +Exit $LASTEXITCODE +# heredoc2 for powershell to ignore block below +$1 = @' +' +: comment end hide powershell-block from Tcl \ +# This comment with closing brace should stay in place whether 'if' commented or not } +: multishell doubled-up cmd exit label - return exitcode +:exit_multishell +:exit_multishell +: \ +@REM @ECHO exitcode: !task_exitcode! +: \ +@IF "is%qstrippedargs:~4,13%"=="isPUNK-ELEVATED" (echo. & @cmd /k echo elevated prompt: type exit to quit) +: \ +@EXIT /B !task_exitcode! +# cmd has exited +: comment end heredoc2 \ +'@ +<# +# id:tailblock0 +# -- powershell multiline comment +#> +<# +no script engine should try to run me +# id:tailblock1 +# + +# +# -- unreachable by tcl directly if ctrl-z character is in the section above. (but file can be read and split on \x1A) +# -- Potential for zip and/or base64 contents, but we can't stop pwsh parser from slurping in the data +# -- so for example a plain text tar archive could cause problems depending on the content. +# -- final line in file must be the powershell multiline comment terminator or other data it can handle. +# -- e.g plain # comment lines will work too +# -- (for example a powershell digital signature is a # commented block of data at the end of the file) +#> + + + diff --git a/src/bootsupport/lib/base64/ascii85.tcl b/src/bootsupport/lib/base64/ascii85.tcl index e05e3430..4fa17c06 100644 --- a/src/bootsupport/lib/base64/ascii85.tcl +++ b/src/bootsupport/lib/base64/ascii85.tcl @@ -6,7 +6,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. -package require Tcl 8.4 +package require Tcl 8.5 9 namespace eval ascii85 { namespace export encode encodefile decode @@ -48,7 +48,7 @@ proc ascii85::encode {args} { must be -maxlen or -wrapchar" } } - + ##nagelfar ignore if {![string is integer -strict $opts(-maxlen)] || $opts(-maxlen) < 0} { return -code error "expected positive integer but got\ @@ -60,8 +60,8 @@ proc ascii85::encode {args} { return "" } - # shorten the names - set ml $opts(-maxlen) + # shorten the names, and normalize numeric values. + set ml [format %d $opts(-maxlen)] set wc $opts(-wrapchar) # if maxlen is zero, don't wrap the output @@ -150,8 +150,7 @@ proc ascii85::encode4bytes {b1 b2 b3 b4} { # This is a convenience command proc ascii85::encodefile {fname} { - set fd [open $fname] - fconfigure $fd -encoding binary -translation binary + set fd [open $fname rb] return [encode [read $fd]][close $fd] } @@ -268,4 +267,4 @@ proc ascii85::pad {chars len padchar} { return $chars } -package provide ascii85 1.0 +package provide ascii85 1.1.1 diff --git a/src/bootsupport/lib/base64/base64.tcl b/src/bootsupport/lib/base64/base64.tcl index fa52c1c3..14a6fbb5 100644 --- a/src/bootsupport/lib/base64/base64.tcl +++ b/src/bootsupport/lib/base64/base64.tcl @@ -19,14 +19,14 @@ # @mdgen EXCLUDE: base64c.tcl -package require Tcl 8.2 +package require Tcl 8.5 9 namespace eval ::base64 { namespace export encode decode } -package provide base64 2.5 +package provide base64 2.6.1 -if {[package vsatisfies [package require Tcl] 8.6]} { +if {[package vsatisfies [package require Tcl] 8.6 9]} { proc ::base64::encode {args} { binary encode base64 -maxlen 76 {*}$args } @@ -180,7 +180,8 @@ if {![catch {package require Trf 2.0}]} { variable base64_tmp variable i - set i 0 + variable i 0 + variable char foreach char {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ a b c d e f g h i j k l m n o p q r s t u v w x y z \ 0 1 2 3 4 5 6 7 8 9 + /} { diff --git a/src/bootsupport/lib/base64/base64c.tcl b/src/bootsupport/lib/base64/base64c.tcl index 29e501df..49a88711 100644 --- a/src/bootsupport/lib/base64/base64c.tcl +++ b/src/bootsupport/lib/base64/base64c.tcl @@ -8,7 +8,7 @@ # @sak notprovided base64c package require critcl -package provide base64c 0.1.0 +package provide base64c 0.1.1 namespace eval ::base64c { variable base64c_rcsid {$Id: base64c.tcl,v 1.5 2008/03/25 07:15:35 andreas_kupries Exp $} diff --git a/src/bootsupport/lib/base64/pkgIndex.tcl b/src/bootsupport/lib/base64/pkgIndex.tcl index c8528f59..83a05a04 100644 --- a/src/bootsupport/lib/base64/pkgIndex.tcl +++ b/src/bootsupport/lib/base64/pkgIndex.tcl @@ -1,5 +1,5 @@ -if {![package vsatisfies [package provide Tcl] 8.2]} {return} -package ifneeded base64 2.5 [list source [file join $dir base64.tcl]] -package ifneeded uuencode 1.1.5 [list source [file join $dir uuencode.tcl]] -package ifneeded yencode 1.1.3 [list source [file join $dir yencode.tcl]] -package ifneeded ascii85 1.0 [list source [file join $dir ascii85.tcl]] +if {![package vsatisfies [package provide Tcl] 8.5 9]} {return} +package ifneeded base64 2.6.1 [list source [file join $dir base64.tcl]] +package ifneeded uuencode 1.1.6 [list source [file join $dir uuencode.tcl]] +package ifneeded yencode 1.1.4 [list source [file join $dir yencode.tcl]] +package ifneeded ascii85 1.1.1 [list source [file join $dir ascii85.tcl]] diff --git a/src/bootsupport/lib/base64/uuencode.tcl b/src/bootsupport/lib/base64/uuencode.tcl index 5e26422d..2b2a9ee3 100644 --- a/src/bootsupport/lib/base64/uuencode.tcl +++ b/src/bootsupport/lib/base64/uuencode.tcl @@ -7,7 +7,7 @@ # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # ------------------------------------------------------------------------- -package require Tcl 8.2; # tcl minimum version +package require Tcl 8.5 9; # tcl minimum version # Try and get some compiled helper package. if {[catch {package require tcllibc}]} { @@ -30,9 +30,9 @@ proc ::uuencode::Encode {s} { if {$c2 == {}} {set c2 0} if {$c3 == {}} {set c3 0} append r [Enc [expr {$c1 >> 2}]] - append r [Enc [expr {(($c1 << 4) & 060) | (($c2 >> 4) & 017)}]] - append r [Enc [expr {(($c2 << 2) & 074) | (($c3 >> 6) & 003)}]] - append r [Enc [expr {($c3 & 077)}]] + append r [Enc [expr {(($c1 << 4) & 0o060) | (($c2 >> 4) & 0o017)}]] + append r [Enc [expr {(($c2 << 2) & 0o074) | (($c3 >> 6) & 0o003)}]] + append r [Enc [expr {($c3 & 0o077)}]] } return $r } @@ -67,27 +67,28 @@ if {[package provide critcl] != {}} { } critcl::ccommand CEncode {dummy interp objc objv} { Tcl_Obj *inputPtr, *resultPtr; - int len, rlen, xtra; + Tcl_Size len, rlen, xtra; unsigned char *input, *p, *r; if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "data"); + Tcl_WrongNumArgs(interp, 1, objv, "data"); /* OK tcl9 */ return TCL_ERROR; } inputPtr = objv[1]; - input = Tcl_GetByteArrayFromObj(inputPtr, &len); + input = Tcl_GetBytesFromObj(interp, inputPtr, &len); /* OK tcl9 */ + if (input == NULL) return TCL_ERROR; if ((xtra = (3 - (len % 3))) != 3) { if (Tcl_IsShared(inputPtr)) inputPtr = Tcl_DuplicateObj(inputPtr); - input = Tcl_SetByteArrayLength(inputPtr, len + xtra); + input = Tcl_SetByteArrayLength(inputPtr, len + xtra); /* OK tcl9 */ memset(input + len, 0, xtra); len += xtra; } rlen = (len / 3) * 4; resultPtr = Tcl_NewObj(); - r = Tcl_SetByteArrayLength(resultPtr, rlen); + r = Tcl_SetByteArrayLength(resultPtr, rlen); /* OK tcl9 */ memset(r, 0, rlen); for (p = input; p < input + len; p += 3) { @@ -104,21 +105,22 @@ if {[package provide critcl] != {}} { critcl::ccommand CDecode {dummy interp objc objv} { Tcl_Obj *inputPtr, *resultPtr; - int len, rlen, xtra; + Tcl_Size len, rlen, xtra; unsigned char *input, *p, *r; if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "data"); + Tcl_WrongNumArgs(interp, 1, objv, "data"); /* OK tcl9 */ return TCL_ERROR; } /* if input is not mod 4, extend it with nuls */ inputPtr = objv[1]; - input = Tcl_GetByteArrayFromObj(inputPtr, &len); + input = Tcl_GetBytesFromObj(interp, inputPtr, &len); /* OK tcl9 */ + if (input == NULL) return TCL_ERROR; if ((xtra = (4 - (len % 4))) != 4) { if (Tcl_IsShared(inputPtr)) inputPtr = Tcl_DuplicateObj(inputPtr); - input = Tcl_SetByteArrayLength(inputPtr, len + xtra); + input = Tcl_SetByteArrayLength(inputPtr, len + xtra); /* OK tcl9 */ memset(input + len, 0, xtra); len += xtra; } @@ -126,7 +128,7 @@ if {[package provide critcl] != {}} { /* output will be 1/3 smaller than input and a multiple of 3 */ rlen = (len / 4) * 3; resultPtr = Tcl_NewObj(); - r = Tcl_SetByteArrayLength(resultPtr, rlen); + r = Tcl_SetByteArrayLength(resultPtr, rlen); /* OK tcl9 */ memset(r, 0, rlen); for (p = input; p < input + len; p += 4) { @@ -181,7 +183,7 @@ if {[info commands ::uuencode::CDecode] != {}} { # ------------------------------------------------------------------------- proc ::uuencode::uuencode {args} { - array set opts {mode 0644 filename {} name {}} + array set opts {mode 0o0644 filename {} name {}} set wrongargs "wrong \# args: should be\ \"uuencode ?-name string? ?-mode octal?\ (-file filename | ?--? string)\"" @@ -258,7 +260,7 @@ proc ::uuencode::uuencode {args} { # data itself. # proc ::uuencode::uudecode {args} { - array set opts {mode 0644 filename {}} + array set opts {mode 0o0644 filename {}} set wrongargs "wrong \# args: should be \"uudecode (-file filename | ?--? string)\"" while {[string match -* [lindex $args 0]]} { switch -glob -- [lindex $args 0] { @@ -324,7 +326,7 @@ proc ::uuencode::uudecode {args} { # ------------------------------------------------------------------------- -package provide uuencode 1.1.5 +package provide uuencode 1.1.6 # ------------------------------------------------------------------------- # diff --git a/src/bootsupport/lib/base64/yencode.tcl b/src/bootsupport/lib/base64/yencode.tcl index 0d4554c0..017085db 100644 --- a/src/bootsupport/lib/base64/yencode.tcl +++ b/src/bootsupport/lib/base64/yencode.tcl @@ -9,7 +9,7 @@ # FUTURE: Rework to allow switching between the tcl/critcl implementations. -package require Tcl 8.2; # tcl minimum version +package require Tcl 8.5 9; # tcl minimum version catch {package require crc32}; # tcllib 1.1 catch {package require tcllibc}; # critcl enhancements for tcllib @@ -65,17 +65,18 @@ if {[package provide critcl] != {}} { } critcl::ccommand CEncode {dummy interp objc objv} { Tcl_Obj *inputPtr, *resultPtr; - int len, rlen, xtra; + Tcl_Size len, rlen, xtra; unsigned char *input, *p, *r, v; if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "data"); + Tcl_WrongNumArgs(interp, 1, objv, "data"); /* OK tcl9 */ return TCL_ERROR; } /* fetch the input data */ inputPtr = objv[1]; - input = Tcl_GetByteArrayFromObj(inputPtr, &len); + input = Tcl_GetBytesFromObj(interp, inputPtr, &len); /* OK tcl9 */ + if (input == NULL) return TCL_ERROR; /* calculate the length of the encoded result */ rlen = len; @@ -87,7 +88,7 @@ if {[package provide critcl] != {}} { /* allocate the output buffer */ resultPtr = Tcl_NewObj(); - r = Tcl_SetByteArrayLength(resultPtr, rlen); + r = Tcl_SetByteArrayLength(resultPtr, rlen); /* OK tcl9 */ /* encode the input */ for (p = input; p < input + len; p++) { @@ -104,21 +105,22 @@ if {[package provide critcl] != {}} { critcl::ccommand CDecode {dummy interp objc objv} { Tcl_Obj *inputPtr, *resultPtr; - int len, rlen, esc; + Tcl_Size len, rlen, esc; unsigned char *input, *p, *r, v; if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "data"); + Tcl_WrongNumArgs(interp, 1, objv, "data"); /* OK tcl9 */ return TCL_ERROR; } /* fetch the input data */ inputPtr = objv[1]; - input = Tcl_GetByteArrayFromObj(inputPtr, &len); + input = Tcl_GetBytesFromObj(interp, inputPtr, &len); /* OK tcl9 */ + if (input == NULL) return TCL_ERROR; /* allocate the output buffer */ resultPtr = Tcl_NewObj(); - r = Tcl_SetByteArrayLength(resultPtr, len); + r = Tcl_SetByteArrayLength(resultPtr, len); /* OK tcl9 */ /* encode the input */ for (p = input, esc = 0, rlen = 0; p < input + len; p++) { @@ -134,7 +136,7 @@ if {[package provide critcl] != {}} { *r++ = v; rlen++; } - Tcl_SetByteArrayLength(resultPtr, rlen); + Tcl_SetByteArrayLength(resultPtr, rlen); /* OK tcl9 */ Tcl_SetObjResult(interp, resultPtr); return TCL_OK; } @@ -192,7 +194,7 @@ proc ::yencode::yencode {args} { } if {$opts(filename) != {}} { - set f [open $opts(filename) r] + set f [open $opts(filename) rb] fconfigure $f -translation binary set data [read $f] close $f @@ -296,7 +298,7 @@ proc ::yencode::ydecode {args} { # ------------------------------------------------------------------------- -package provide yencode 1.1.3 +package provide yencode 1.1.4 # ------------------------------------------------------------------------- # diff --git a/src/bootsupport/lib/virtchannel_base/cat.tcl b/src/bootsupport/lib/virtchannel_base/cat.tcl new file mode 100644 index 00000000..28a287a1 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/cat.tcl @@ -0,0 +1,135 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2011,2019 Andreas Kupries + +# Facade concatenating the contents of the channels it was constructed +# with. Owns the sub-ordinate channels and closes them on exhaustion and/or +# when closed itself. + +# @@ Meta Begin +# Package tcl::chan::cat 1.0.4 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2011 +# Meta as::license BSD +# Meta description Facade concatenating the contents of the channels it +# Meta description was constructed with. Owns the sub-ordinate channels +# Meta description and closes them on exhaustion and/or when closed itself. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::core +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::cat {args} { + return [::chan create {read} [cat::implementation new {*}$args]] +} + +oo::class create ::tcl::chan::cat::implementation { + superclass ::tcl::chan::core ; # -> initialize, finalize. + + # We are not using the standard event handling class, because here + # it will not be timer-driven. We propagate anything related to + # events to catin and catout instead and let them handle things. + + constructor {args} { + set channels $args + # Disable translation (and hence encoding) in the wrapped channels. + # This will happen in our generic layer instead. + foreach c $channels { + fconfigure $c -translation binary + } + set delay 10 + set watching 0 + return + } + + destructor { + foreach c $channels { + ::close $c + } + return + } + + variable channels timer delay watching + + method watch {c requestmask} { + if {"read" in $requestmask} { + # Activate event handling. Either drive an eof home via + # timers, or activate things in the foremost sub-ordinate. + + set watching 1 + if {![llength $channels]} { + set timer [after $delay [namespace code [list my Post $c]]] + } else { + chan event [lindex $channels 0] readable [list chan postevent $c read] + } + } else { + # Stop events. Either kill timer, or disable in the + # foremost sub-ordinate. + + set watching 0 + if {![llength $channels]} { + catch { after cancel $timer } + } else { + chan event [lindex $channels 0] readable {} + } + } + return + } + + method read {c n} { + if {![llength $channels]} { + # This signals EOF higher up. + return {} + } + + set buf {} + while {([string length $buf] < $n) && + [llength $channels]} { + + set in [lindex $channels 0] + set toread [expr {$n - [string length $buf]}] + append buf [::read $in $toread] + + if {[eof $in]} { + close $in + set channels [lrange $channels 1 end] + + # The close of the exhausted subordinate killed any + # fileevent handling we may have had attached to this + # channel. Update the settings (i.e. move to the next + # subordinate, or to timer-based, to drive the eof + # home). + + if {$watching} { + my watch $c read + } + } + } + + # When `buf` is empty, all channels have been exhausted and + # closed, therefore returning this empty string will cause an + # EOF higher up. + return $buf + } + + method Post {c} { + set timer [after $delay [namespace code [list my Post $c]]] + chan postevent $c read + return + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::cat 1.0.4 +return diff --git a/src/bootsupport/lib/virtchannel_base/facade.tcl b/src/bootsupport/lib/virtchannel_base/facade.tcl new file mode 100644 index 00000000..d738446f --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/facade.tcl @@ -0,0 +1,234 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2011 Andreas Kupries + +# Facade wrapping around some other channel. All operations on the +# facade are delegated to the wrapped channel. This makes it useful +# for debugging of Tcl's activity on a channel. While a transform can +# be used for that as well it does not have access to some things of +# the base-channel, i.e. all the event managment is not visible to it, +# whereas the facade has access to even this. + +# @@ Meta Begin +# Package tcl::chan::facade 1.0.2 +# Meta as::author {Colin McCormack} +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2011 +# Meta as::license BSD +# Meta description Facade wrapping around some other channel. All +# Meta description operations on the facade are delegated to the +# Meta description wrapped channel. This makes it useful for debugging +# Meta description of Tcl's activity on a channel. While a transform +# Meta description can be used for that as well it does not have +# Meta description access to some things of the base-channel, i.e. all +# Meta description the event managment is not visible to it, whereas +# Meta description the facade has access to even this. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::core +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# +## TODO document the special options of the facade +## TODO log integration. +## TODO document that facada takes ownership of the channel. + +package require Tcl 8.5 9 +package require TclOO +package require logger +package require tcl::chan::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +logger::initNamespace ::tcl::chan::facade +proc ::tcl::chan::facade {args} { + return [::chan create {read} [facade::implementation new {*}$args]] +} + +# # ## ### ##### ######## ############# + +oo::class create ::tcl::chan::facade::implementation { + superclass ::tcl::chan::core ; # -> initialize, finalize. + + # # ## ### ##### ######## ############# + + # We are not using the standard event handling class, because here + # it will not be timer-driven. We propagate anything related to + # events to the wrapped channel instead and let it handle things. + + constructor {thechan} { + # Access to the log(ger) commands. + namespace path [list {*}[namespace path] ::tcl::chan::facade] + + set chan $thechan + + # set some configuration data + set created [clock milliseconds] + set used 0 + set user "" ;# user data - freeform + + # validate args + if {$chan eq [self]} { + return -code error "recursive chan! No good." + } elseif {$chan eq ""} { + return -code error "Needs a chan argument" + } + + set blocking [::chan configure $chan -blocking] + return + } + + destructor { + log::debug {[self] destroyed} + if {[catch { ::chan close $chan } e o]} { + log::debug {failed to close $chan [self] because "$e" ($o)} + } + return + } + + variable chan used user created blocking + + method initialize {myself mode} { + log::debug {$myself initialize $chan $mode} + log::debug {$chan configured: ([::chan configure $chan])} + return [next $chan $mode] + } + + method finalize {myself} { + log::debug {$myself finalize $chan} + catch {::chan close $chan} + catch {next $myself} + catch {my destroy} + return + } + + method blocking {myself mode} { + if {[catch { + ::chan configure $chan -blocking $mode + set blocking $mode + } e o]} { + log::debug {$myself blocking $chan $mode -> error $e ($o)} + } else { + log::debug {$myself blocking $chan $mode -> $e} + } + return + } + + method watch {myself requestmask} { + log::debug {$myself watch $chan $requestmask} + + if {"read" in $requestmask} { + fileevent readable $chan [my Callback Readable $myself] + } else { + fileevent readable $chan {} + } + + if {"write" in $requestmask} { + fileevent writable $chan [my Callback Writable $myself] + } else { + fileevent writable $chan {} + } + return + } + + method read {myself n} { + log::debug {$myself read $chan begin eof: [::chan eof $chan], blocked: [::chan blocked $chan]} + set used [clock milliseconds] + + if {[catch { + set data [::chan read $chan $n] + } e o]} { + log::error {$myself read $chan $n -> error $e ($o)} + } else { + log::debug {$myself read $chan $n -> [string length $data] bytes: [string map {\n \\n} "'[string range $data 0 20]...[string range $data end-20 end]"]'} + log::debug {$myself read $chan eof = [::chan eof $chan]} + log::debug {$myself read $chan blocked = [::chan blocked $chan]} + log::debug {$chan configured: ([::chan configure $chan])} + + set gone [catch {chan eof $chan} eof] + if { + ($data eq {}) && + !$gone && !$eof && !$blocking + } { + log::error {$myself EAGAIN} + return -code error EAGAIN + } + } + + log::debug {$myself read $chan result: [string length $data] bytes} + return $data + } + + method write {myself data} { + log::debug {$myself write $chan [string length $data] / [::chan pending output $chan] / [::chan pending output $myself]} + set used [clock milliseconds] + ::chan puts -nonewline $chan $data + return [string length $data] + } + + method configure {myself option value} { + log::debug {[self] configure $myself $option -> $value} + + if {$option eq "-user"} { + set user $value + return + } + + ::chan configure $fd $option $value + return + } + + method cget {myself option} { + switch -- $option { + -self { return [self] } + -fd { return $chan } + -used { return $used } + -created { return $created } + -user { return $user } + default { + return [::chan configure $chan $option] + } + } + } + + method cgetall {myself} { + set result [::chan configure $chan] + lappend result \ + -self [self] \ + -fd $chan \ + -used $used \ + -created $created \ + -user $user + + log::debug {[self] cgetall $myself -> $result} + return $result + } + + # # ## ### ##### ######## ############# + + # Internals. Methods. Event generation. + method Readable {myself} { + log::debug {$myself readable $chan - [::chan pending input $chan]} + ::chan postevent $myself read + return + } + + method Writable {myself} { + log::debug {$myself writable $chan - [::chan pending output $chan]} + ::chan postevent $myself write + return + } + + method Callback {method args} { + list [uplevel 1 {namespace which my}] $method {*}$args + } + + # # ## ### ##### ######## ############# +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::facade 1.0.2 +return diff --git a/src/bootsupport/lib/virtchannel_base/fifo.tcl b/src/bootsupport/lib/virtchannel_base/fifo.tcl new file mode 100644 index 00000000..5f04aafb --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/fifo.tcl @@ -0,0 +1,138 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::fifo 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Re-implementation of Memchan's fifo +# Meta description channel. Based on Tcl 8.5's channel +# Meta description reflection support. Exports a single +# Meta description command for the creation of new +# Meta description channels. No arguments. Result is the +# Meta description handle of the new channel. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::events + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::fifo {} { + return [::chan create {read write} [fifo::implementation new]] +} + +oo::class create ::tcl::chan::fifo::implementation { + superclass ::tcl::chan::events ; # -> initialize, finalize, watch + + method initialize {args} { + my allow write + next {*}$args + } + + method read {c n} { + set max [string length $read] + set last [expr {$at + $n - 1}] + set result {} + + # last+1 <= max + # <=> at+n <= max + # <=> n <= max-at + + if {$n <= ($max - $at)} { + # The request is less than what we have left in the read + # buffer, we take it, and move the read pointer forward. + + append result [string range $read $at $last] + incr at $n + incr $size -$n + } else { + # We need the whole remaining read buffer, and more. For + # the latter we shift the write buffer contents over into + # the read buffer, and then read from the latter again. + + append result [string range $read $at end] + incr n -[string length $result] + + set at 0 + set read $write + set write {} + set size [string length $read] + set max $size + + # at == 0 + if {$n <= $max} { + # The request is less than what we have in the updated + # read buffer, we take it, and move the read pointer + # forward. + + append result [string range $read 0 $last] + set at $n + incr $size -$n + } else { + # We need the whole remaining read buffer, and + # more. As we took the data from write already we have + # nothing left, and update accordingly. + + append result $read + + set at 0 + set read {} + set size 0 + } + } + + my Readable + + if {$result eq {}} { + return -code error EAGAIN + } + + return $result + } + + method write {c bytes} { + append write $bytes + set n [string length $bytes] + incr size $n + my Readable + return $n + } + + # # ## ### ##### ######## ############# + + variable at read write size + + # # ## ### ##### ######## ############# + + constructor {} { + set at 0 + set read {} + set write {} + set size 0 + next + } + + method Readable {} { + if {$size} { + my allow read + } else { + my disallow read + } + return + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::fifo 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_base/fifo2.tcl b/src/bootsupport/lib/virtchannel_base/fifo2.tcl new file mode 100644 index 00000000..8de162e2 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/fifo2.tcl @@ -0,0 +1,113 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::fifo2 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::notes This fifo2 command does not have to +# Meta as::notes deal with the pesky details of +# Meta as::notes threading for cross-thread +# Meta as::notes communication. That is hidden in the +# Meta as::notes implementation of reflected +# Meta as::notes channels. It is less optimal as the +# Meta as::notes command provided by Memchan as this +# Meta as::notes fifo2 may involve three threads when +# Meta as::notes sending data around: The threads the +# Meta as::notes two endpoints are in, and the thread +# Meta as::notes holding this code. Memchan's C +# Meta as::notes implementation does not need this last +# Meta as::notes intermediary thread. +# Meta description Re-implementation of Memchan's fifo2 +# Meta description channel. Based on Tcl 8.5's channel +# Meta description reflection support. Exports a single +# Meta description command for the creation of new +# Meta description channels. No arguments. Result are the +# Meta description handles of the two new channels. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::halfpipe +# Meta require {Tcl 8.5} +# @@ Meta End +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::halfpipe + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::fifo2 {} { + + set coordinator [fifo2::implementation new] + + lassign [halfpipe \ + -write-command [list $coordinator froma] \ + -close-command [list $coordinator closeda]] \ + a ha + + lassign [halfpipe \ + -write-command [list $coordinator fromb] \ + -close-command [list $coordinator closedb]] \ + b hb + + $coordinator connect $a $ha $b $hb + + return [list $a $b] +} + +oo::class create ::tcl::chan::fifo2::implementation { + method connect {thea theha theb thehb} { + set a $thea + set b $theb + set ha $theha + set hb $thehb + return + } + + method closeda {c} { + set a {} + if {$b ne {}} { + close $b + set b {} + } else { + my destroy + } + return + } + + method closedb {c} { + set b {} + if {$a ne {}} { + close $a + set a {} + } else { + my destroy + } + return + } + + method froma {c bytes} { + $hb put $bytes + return + } + + method fromb {c bytes} { + $ha put $bytes + return + } + + # # ## ### ##### ######## ############# + + variable a b ha hb + + # # ## ### ##### ######## ############# +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::fifo2 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_base/halfpipe.tcl b/src/bootsupport/lib/virtchannel_base/halfpipe.tcl new file mode 100644 index 00000000..845218e5 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/halfpipe.tcl @@ -0,0 +1,194 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009, 2019 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::halfpipe 1.0.3 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009,2019 +# Meta as::license BSD +# Meta description Implementation of one half of a pipe +# Meta description channel. Based on Tcl 8.5's channel +# Meta description reflection support. Exports a single +# Meta description command for the creation of new +# Meta description channels. Option arguments. Result is the +# Meta description handle of the new channel, and the object +# Meta description command of the handler object. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::events + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::halfpipe {args} { + set handler [halfpipe::implementation new {*}$args] + return [list [::chan create {read write} $handler] $handler] +} + +oo::class create ::tcl::chan::halfpipe::implementation { + superclass ::tcl::chan::events ; # -> initialize, finalize, watch + + method initialize {args} { + my allow write + set eof 0 + next {*}$args + } + + method finalize {c} { + my Call -close-command $c + next $c + } + + method read {c n} { + set max [string length $read] + set last [expr {$at + $n - 1}] + set result {} + + # last+1 <= max + # <=> at+n <= max + # <=> n <= max-at + + if {$n <= ($max - $at)} { + # There is enough data in the buffer to fill the request, so take + # it from there and move the read pointer forward. + + append result [string range $read $at $last] + incr at $n + incr $size -$n + } else { + # We need the whole remaining read buffer, and more. For + # the latter we make the write buffer the new read buffer, + # and then read from it again. + + append result [string range $read $at end] + incr n -[string length $result] + + set at 0 + set last [expr {$n - 1}] + set read $write + set write {} + set size [string length $read] + set max $size + + # at == 0 simplifies expressions + if {$n <= $max} { + # The request is less than what we have in the new + # read buffer, we take it, and move the read pointer + # forward. + + append result [string range $read 0 $last] + set at $n + incr $size -$n + } else { + # We need the whole remaining read buffer, and + # more. As we took the data from write already we have + # nothing left, and update accordingly. + + append result $read + + set at 0 + set read {} + set size 0 + } + } + my Readable + if {$result eq {} && !$eof} { + return -code error EAGAIN + } + return $result + } + + method write {c bytes} { + my Call -write-command $c $bytes + return [string length $bytes] + } + + # # ## ### ##### ######## ############# + + method put bytes { + append write $bytes + set n [string length $bytes] + if {$n == 0} { + my variable eof + set eof 1 + } else { + incr size $n + } + my Readable + return $n + } + + # # ## ### ##### ######## ############# + + variable at eof read write size options + # at : first location in read buffer not yet read + # eof : indicates whether the end of the data has been reached + # read : read buffer + # write : buffer for received data, i.e. + # written into the halfpipe from + # the other side. + # size : combined length of receive and read buffers + # == amount of stored data + # options : configuration array + + # The halpipe uses a pointer (`at`) into the data buffer to + # extract the characters read by the user, while not shifting the + # data down in memory. Doing such a shift would cause a large + # performance hit (O(n**2) operation vs O(n)). This however comes + # with the danger of the buffer growing out of bounds as ever more + # data is appended by the receiver while the reader is not + # catching up, preventing a release. The solution to this in turn + # is to split the buffer into two. An append-only receive buffer + # (`write`) for incoming data, and a `read` buffer with the + # pointer. When the current read buffer is entirely consumed the + # current receive buffer becomes the new read buffer and a new + # empty receive buffer is started. + + # # ## ### ##### ######## ############# + + constructor {args} { + array set options { + -write-command {} + -empty-command {} + -close-command {} + } + # todo: validity checking of options (legal names, legal + # values, etc.) + array set options $args + set at 0 + set read {} + set write {} + set size 0 + next + } + + method Readable {} { + if {$size || $eof} { + my allow read + } else { + my variable channel + my disallow read + my Call -empty-command $channel + } + return + } + + method Call {o args} { + if {![llength $options($o)]} return + uplevel \#0 [list {*}$options($o) {*}$args] + return + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::halfpipe 1.0.3 +return diff --git a/src/bootsupport/lib/virtchannel_base/memchan.tcl b/src/bootsupport/lib/virtchannel_base/memchan.tcl new file mode 100644 index 00000000..76ef0b64 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/memchan.tcl @@ -0,0 +1,173 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# Variable string channel (in-memory r/w file, internal variable). +# Seekable beyond the end of the data, implies appending of 0x00 +# bytes. + +# @@ Meta Begin +# Package tcl::chan::memchan 1.0.5 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Re-implementation of Memchan's memchan +# Meta description channel. Based on Tcl 8.5's channel +# Meta description reflection support. Exports a single +# Meta description command for the creation of new +# Meta description channels. No arguments. Result is the +# Meta description handle of the new channel. Essentially +# Meta description an in-memory read/write random-access +# Meta description file. Similar to -> tcl::chan::variable, +# Meta description except the content variable is internal, +# Meta description part of the channel. Further similar to +# Meta description -> tcl::chan::string, except that the +# Meta description content is here writable, and +# Meta description extendable. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +try { + package require tcl::oo +} trap {TCL PACKAGE UNFOUND} {tres topts} { + package require TclOO +} +package require tcl::chan::events + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::memchan {} { + return [::chan create {read write} [memchan::implementation new]] +} + +oo::class create ::tcl::chan::memchan::implementation { + superclass ::tcl::chan::events ; # -> initialize, finalize, watch + + constructor {} { + set content {} + set at 0 + next + } + + method initialize {args} { + my allow write + my Events + next {*}$args + } + + variable content at + + method read {c n} { + # First determine the location of the last byte to read, + # relative to the current location, and limited by the maximum + # location we are allowed to access per the size of the + # content. + + set last [expr {min($at + $n,[string length $content])-1}] + + # Then extract the relevant range from the content, move the + # seek location behind it, and return the extracted range. Not + # to forget, switch readable events based on the seek + # location. + + set res [string range $content $at $last] + set at $last + incr at + + my Events + return $res + } + + method write {c newbytes} { + # Return immediately if there is nothing is to write. + set n [string length $newbytes] + if {$n == 0} { + return $n + } + + # Determine where and how to write. There are three possible cases. + # (1) Append at/after the end. + # (2) Starting in the middle, but extending beyond the end. + # (3) Replace in the middle. + + set max [string length $content] + if {$at >= $max} { + # Ad 1. + append content $newbytes + set at [string length $content] + } else { + set last [expr {$at + $n - 1}] + if {$last >= $max} { + # Ad 2. + set content [string replace $content $at end $newbytes] + set at [string length $content] + } else { + # Ad 3. + set content [string replace $content $at $last $newbytes] + set at $last + incr at + } + } + + my Events + return $n + } + + method seek {c offset base} { + # offset == 0 && base == current + # <=> Seek nothing relative to current + # <=> Report current location. + + if {!$offset && ($base eq "current")} { + return $at + } + + # Compute the new location per the arguments. + + set max [string length $content] + switch -exact -- $base { + start { set newloc $offset} + current { set newloc [expr {$at + $offset }] } + end { set newloc [expr {$max + $offset }] } + } + + # Check if the new location is beyond the range given by the + # content. + + if {$newloc < 0} { + return -code error "Cannot seek before the start of the channel" + } elseif {$newloc > $max} { + # We can seek beyond the end of the current contents, add + # a block of zeros. + #puts XXX.PAD.[expr {$newloc - $max}] + append content [binary format @[expr {$newloc - $max}]] + } + + # Commit to new location, switch readable events, and report. + set at $newloc + + my Events + return $at + } + + method Events {} { + # Always readable -- Even if the seek location is at the end + # (or beyond). In that case the readable events are fired + # endlessly until the eof indicated by the seek location is + # properly processed by the event handler. Like for regular + # files -- Ticket [864a0c83e3]. + my allow read + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::memchan 1.0.5 +return diff --git a/src/bootsupport/lib/virtchannel_base/null.tcl b/src/bootsupport/lib/virtchannel_base/null.tcl new file mode 100644 index 00000000..da9d7348 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/null.tcl @@ -0,0 +1,54 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::null 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Re-implementation of Memchan's null +# Meta description channel. Based on Tcl 8.5's channel +# Meta description reflection support. Exports a single +# Meta description command for the creation of new +# Meta description channels. No arguments. Result is the +# Meta description handle of the new channel. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::events + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::null {} { + return [::chan create {write} [null::implementation new]] +} + +oo::class create ::tcl::chan::null::implementation { + superclass ::tcl::chan::events ; # -> initialize, finalize, watch + + method initialize {args} { + my allow write + next {*}$args + } + + # Ignore the data in most particulars. We do count it so that we + # can tell the caller that everything was written. Null device. + + method write {c data} { + return [string length $data] + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::null 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_base/nullzero.tcl b/src/bootsupport/lib/virtchannel_base/nullzero.tcl new file mode 100644 index 00000000..c217657a --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/nullzero.tcl @@ -0,0 +1,62 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::nullzero 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Implementation of a channel combining +# Meta description Memchan's null and zero channels in a +# Meta description single device. Based on Tcl 8.5's channel +# Meta description reflection support. Exports a single +# Meta description command for the creation of new +# Meta description channels. No arguments. Result is the +# Meta description handle of the new channel. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::events + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::nullzero {} { + return [::chan create {read write} [nullzero::implementation new]] +} + +oo::class create ::tcl::chan::nullzero::implementation { + superclass ::tcl::chan::events ; # -> initialize, finalize, watch + + method initialize {args} { + my allow read write + next {*}$args + } + + # Ignore the data in most particulars. We do count it so that we + # can tell the caller that everything was written. Null device. + + method write {c data} { + return [string length $data] + } + + # Generate and return a block of N null bytes, as requested. Zero + # device. + + method read {c n} { + return [binary format @$n] + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::nullzero 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_base/pkgIndex.tcl b/src/bootsupport/lib/virtchannel_base/pkgIndex.tcl new file mode 100644 index 00000000..c8431a2b --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/pkgIndex.tcl @@ -0,0 +1,17 @@ +if {![package vsatisfies [package provide Tcl] 8.5 9]} {return} + +package ifneeded tcl::chan::cat 1.0.4 [list source [file join $dir cat.tcl]] +package ifneeded tcl::chan::facade 1.0.2 [list source [file join $dir facade.tcl]] +package ifneeded tcl::chan::fifo 1.1 [list source [file join $dir fifo.tcl]] +package ifneeded tcl::chan::fifo2 1.1 [list source [file join $dir fifo2.tcl]] +package ifneeded tcl::chan::halfpipe 1.0.3 [list source [file join $dir halfpipe.tcl]] +package ifneeded tcl::chan::memchan 1.0.5 [list source [file join $dir memchan.tcl]] +package ifneeded tcl::chan::null 1.1 [list source [file join $dir null.tcl]] +package ifneeded tcl::chan::nullzero 1.1 [list source [file join $dir nullzero.tcl]] +package ifneeded tcl::chan::random 1.1 [list source [file join $dir random.tcl]] +package ifneeded tcl::chan::std 1.0.2 [list source [file join $dir std.tcl]] +package ifneeded tcl::chan::string 1.0.4 [list source [file join $dir string.tcl]] +package ifneeded tcl::chan::textwindow 1.1 [list source [file join $dir textwindow.tcl]] +package ifneeded tcl::chan::variable 1.0.5 [list source [file join $dir variable.tcl]] +package ifneeded tcl::chan::zero 1.1 [list source [file join $dir zero.tcl]] +package ifneeded tcl::randomseed 1.1 [list source [file join $dir randseed.tcl]] diff --git a/src/bootsupport/lib/virtchannel_base/random.tcl b/src/bootsupport/lib/virtchannel_base/random.tcl new file mode 100644 index 00000000..c1778b8a --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/random.tcl @@ -0,0 +1,80 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::random 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Implementation of a channel similar to +# Meta description Memchan's random channel. Based on Tcl +# Meta description 8.5's channel reflection support. Exports +# Meta description a single command for the creation of new +# Meta description channels. One argument, a list of +# Meta description numbers to initialize the feedback +# Meta description register of the internal random number +# Meta description generator. Result is the handle of the +# Meta description new channel. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require tcl::chan::events +package require Tcl 8.5 9 +package require TclOO + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::random {seed} { + return [::chan create {read} [random::implementation new $seed]] +} + +oo::class create ::tcl::chan::random::implementation { + superclass tcl::chan::events ; # -> initialize, finalize, watch + + constructor {theseed} { + my variable seed next + set seed $theseed + set next [expr "([join $seed +]) & 0xff"] + next + } + + method initialize {args} { + my allow read + next {*}$args + } + + # Generate and return a block of N randomly selected bytes, as + # requested. Random device. + + method read {c n} { + set buffer {} + while {$n} { + append buffer [binary format c [my Next]] + incr n -1 + } + return $buffer + } + + variable seed + variable next + + method Next {} { + my variable seed next + set result $next + set next [expr {(2*$next - [lindex $seed 0]) & 0xff}] + set seed [linsert [lrange $seed 1 end] end $result] + return $result + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::random 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_base/randseed.tcl b/src/bootsupport/lib/virtchannel_base/randseed.tcl new file mode 100644 index 00000000..5e0cbed6 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/randseed.tcl @@ -0,0 +1,58 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::randomseed 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Generate and combine seed lists for the +# Meta description random number generator inside of the +# Meta description tcl::chan::random channel. Sources of +# Meta description randomness are process id, time in two +# Meta description granularities, and Tcl's random number +# Meta description generator. +# Meta platform tcl +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 + +# # ## ### ##### ######## ############# + +namespace eval ::tcl {} + +proc ::tcl::randomseed {} { + set result {} + foreach v [list \ + [pid] \ + [clock seconds] \ + [expr {int(256*rand())}] \ + [clock clicks -milliseconds]] \ + { + lappend result [expr {$v % 256}] + } + return $result +} + +proc ::tcl::combine {a b} { + while {[llength $a] < [llength $b]} { + lappend a 0 + } + while {[llength $b] < [llength $a]} { + lappend b 0 + } + + set result {} + foreach x $a y $b { + lappend result [expr {($x ^ $y) % 256}] + } + return $result +} + +# # ## ### ##### ######## ############# +package provide tcl::randomseed 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_base/std.tcl b/src/bootsupport/lib/virtchannel_base/std.tcl new file mode 100644 index 00000000..24927e30 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/std.tcl @@ -0,0 +1,97 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2011 Andreas Kupries + +# Facade wrapping the separate channels for stdin and stdout into a +# single read/write channel for all regular standard i/o. Not +# seekable. Fileevent handling is propagated to the regular channels +# the facade wrapped about. Only one instance of the class is +# ever created. + +# @@ Meta Begin +# Package tcl::chan::std 1.0.2 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2011 +# Meta as::license BSD +# Meta description Facade wrapping the separate channels for stdin +# Meta description and stdout into a single read/write channel for +# Meta description all regular standard i/o. Not seekable. Only one +# Meta description instance of the class is ever created. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::core +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::std {} { + ::variable std + if {$std eq {}} { + set std [::chan create {read write} [std::implementation new]] + } + return $std +} + +oo::class create ::tcl::chan::std::implementation { + superclass ::tcl::chan::core ; # -> initialize, finalize. + + # We are not using the standard event handling class, because here + # it will not be timer-driven. We propagate anything related to + # events to stdin and stdout instead and let them handle things. + + constructor {} { + # Disable encoding and translation processing in the wrapped channels. + # This will happen in our generic layer instead. + fconfigure stdin -translation binary + fconfigure stdout -translation binary + return + } + + method watch {c requestmask} { + + if {"read" in $requestmask} { + fileevent readable stdin [list chan postevent $c read] + } else { + fileevent readable stdin {} + } + + if {"write" in $requestmask} { + fileevent readable stdin [list chan postevent $c write] + } else { + fileevent readable stdout {} + } + + return + } + + method read {c n} { + # Read is redirected to stdin. + return [::read stdin $n] + } + + method write {c newbytes} { + # Write is redirected to stdout. + puts -nonewline stdout $newbytes + flush stdout + return [string length $newbytes] + } +} + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan { + ::variable std {} +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::std 1.0.2 +return diff --git a/src/bootsupport/lib/virtchannel_base/string.tcl b/src/bootsupport/lib/virtchannel_base/string.tcl new file mode 100644 index 00000000..b3b3f85c --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/string.tcl @@ -0,0 +1,126 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::string 1.0.4 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Implementation of a channel representing +# Meta description an in-memory read-only random-access +# Meta description file. Based on using Tcl 8.5's channel +# Meta description reflection support. Exports a single +# Meta description command for the creation of new channels. +# Meta description One argument, the contents of the file. +# Meta description Result is the handle of the new channel. +# Meta description Similar to -> tcl::chan::memchan, except +# Meta description that the content is read-only. Seekable +# Meta description only within the bounds of the content. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +if {[catch {package require tcl::oo}]} { + package require TclOO +} +package require tcl::chan::events + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::string {content} { + return [::chan create {read} [string::implementation new $content]] +} + +oo::class create ::tcl::chan::string::implementation { + superclass ::tcl::chan::events ; # -> initialize, finalize, watch + + constructor {thecontent} { + set content $thecontent + set at 0 + next + } + + method initialize {args} { + my Events + next {*}$args + } + + variable content at + + method read {c n} { + + # First determine the location of the last byte to read, + # relative to the current location, and limited by the maximum + # location we are allowed to access per the size of the + # content. + + set last [expr {min($at + $n,[string length $content])-1}] + + # Then extract the relevant range from the content, move the + # seek location behind it, and return the extracted range. Not + # to forget, switch readable events based on the seek + # location. + + set res [string range $content $at $last] + set at $last + incr at + + my Events + return $res + } + + method seek {c offset base} { + # offset == 0 && base == current + # <=> Seek nothing relative to current + # <=> Report current location. + + if {!$offset && ($base eq "current")} { + return $at + } + + # Compute the new location per the arguments. + + set max [string length $content] + switch -exact -- $base { + start { set newloc $offset} + current { set newloc [expr {$at + $offset }] } + end { set newloc [expr {$max + $offset }] } + } + + # Check if the new location is beyond the range given by the + # content. + + if {$newloc < 0} { + return -code error "Cannot seek before the start of the channel" + } elseif {$newloc > $max} { + return -code error "Cannot seek after the end of the channel" + } + + # Commit to new location, switch readable events, and report. + set at $newloc + + my Events + return $at + } + + method Events {} { + # Always readable -- Even if the seek location is at the end + # (or beyond). In that case the readable events are fired + # endlessly until the eof indicated by the seek location is + # properly processed by the event handler. Like for regular + # files -- Ticket [864a0c83e3]. + my allow read + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::string 1.0.4 +return diff --git a/src/bootsupport/lib/virtchannel_base/textwindow.tcl b/src/bootsupport/lib/virtchannel_base/textwindow.tcl new file mode 100644 index 00000000..4e23a37a --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/textwindow.tcl @@ -0,0 +1,74 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::textwindow 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::credit To Bryan Oakley for rotext, see +# Meta as::credit http://wiki.tcl.tk/22036. His code was +# Meta as::credit used here as template for the text +# Meta as::credit widget portions of the channel. +# Meta description Implementation of a text window +# Meta description channel, using Tcl 8.5's channel +# Meta description reflection support. Exports a single +# Meta description command for the creation of new +# Meta description channels. No arguments. Result is the +# Meta description handle of the new channel. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::events + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::textwindow {w} { + set chan [::chan create {write} [textwindow::implementation new $w]] + fconfigure $chan -encoding utf-8 -buffering none + return $chan +} + +oo::class create ::tcl::chan::textwindow::implementation { + superclass ::tcl::chan::events ; # -> initialize, finalize, watch + + constructor {w} { + set widget $w + next + } + + # # ## ### ##### ######## ############# + + variable widget + + # # ## ### ##### ######## ############# + + method initialize {args} { + my allow write + next {*}$args + } + + method write {c data} { + # NOTE: How is encoding convertfrom dealing with a partial + # utf-8 character at the end of the buffer ? Should be saved + # up for the next buffer. No idea if we can. + + $widget insert end [encoding convertfrom utf-8 $data] + $widget see end + return [string length $data] + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::textwindow 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_base/variable.tcl b/src/bootsupport/lib/virtchannel_base/variable.tcl new file mode 100644 index 00000000..0c65a376 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/variable.tcl @@ -0,0 +1,181 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::variable 1.0.5 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Implementation of a channel representing +# Meta description an in-memory read-write random-access +# Meta description file. Based on Tcl 8.5's channel reflection +# Meta description support. Exports a single command for the +# Meta description creation of new channels. No arguments. +# Meta description Result is the handle of the new channel. +# Meta description Similar to -> tcl::chan::memchan, except +# Meta description that the variable holding the content +# Meta description exists outside of the channel itself, in +# Meta description some namespace, and as such is not a part +# Meta description of the channel. Seekable beyond the end +# Meta description of the data, implies appending of 0x00 +# Meta description bytes. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::events + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::variable {varname} { + return [::chan create {read write} [variable::implementation new $varname]] +} + +oo::class create ::tcl::chan::variable::implementation { + superclass ::tcl::chan::events ; # -> initialize, finalize, watch + + constructor {thevarname} { + set varname $thevarname + set at 0 + + upvar #0 $varname content + if {![info exists content]} { + set content {} + } + next + } + + method initialize {args} { + my allow write + my Events + next {*}$args + } + + variable varname at + + method read {c n} { + # Bring connected variable for content into scope. + + upvar #0 $varname content + + # First determine the location of the last byte to read, + # relative to the current location, and limited by the maximum + # location we are allowed to access per the size of the + # content. + + set last [expr {min($at + $n,[string length $content])-1}] + + # Then extract the relevant range from the content, move the + # seek location behind it, and return the extracted range. Not + # to forget, switch readable events based on the seek + # location. + + set res [string range $content $at $last] + set at $last + incr at + + my Events + return $res + } + + method write {c newbytes} { + # Bring connected variable for content into scope. + + upvar #0 $varname content + + # Return immediately if there is nothing is to write. + set n [string length $newbytes] + if {$n == 0} { + return $n + } + + # Determine where and how to write. There are three possible cases. + # (1) Append at/after the end. + # (2) Starting in the middle, but extending beyond the end. + # (3) Replace in the middle. + + set max [string length $content] + if {$at >= $max} { + # Ad 1. + append content $newbytes + set at [string length $content] + } else { + set last [expr {$at + $n - 1}] + if {$last >= $max} { + # Ad 2. + set content [string replace $content $at end $newbytes] + set at [string length $content] + } else { + # Ad 3. + set content [string replace $content $at $last $newbytes] + set at $last + incr at + } + } + + my Events + return $n + } + + method seek {c offset base} { + # offset == 0 && base == current + # <=> Seek nothing relative to current + # <=> Report current location. + + if {!$offset && ($base eq "current")} { + return $at + } + + # Bring connected variable for content into scope. + + upvar #0 $varname content + + # Compute the new location per the arguments. + + set max [string length $content] + switch -exact -- $base { + start { set newloc $offset} + current { set newloc [expr {$at + $offset }] } + end { set newloc [expr {$max + $offset }] } + } + + # Check if the new location is beyond the range given by the + # content. + + if {$newloc < 0} { + return -code error "Cannot seek before the start of the channel" + } elseif {$newloc > $max} { + # We can seek beyond the end of the current contents, add + # a block of zeros. + append content [binary format @[expr {$newloc - $max}]] + } + + # Commit to new location, switch readable events, and report. + set at $newloc + + my Events + return $at + } + + method Events {} { + # Always readable -- Even if the seek location is at the end + # (or beyond). In that case the readable events are fired + # endlessly until the eof indicated by the seek location is + # properly processed by the event handler. Like for regular + # files -- Ticket [864a0c83e3]. + my allow read + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::variable 1.0.5 +return diff --git a/src/bootsupport/lib/virtchannel_base/zero.tcl b/src/bootsupport/lib/virtchannel_base/zero.tcl new file mode 100644 index 00000000..752e109e --- /dev/null +++ b/src/bootsupport/lib/virtchannel_base/zero.tcl @@ -0,0 +1,54 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::zero 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Re-implementation of Memchan's zero +# Meta description channel. Based on Tcl 8.5's channel +# Meta description reflection support. Exports a single +# Meta description command for the creation of new +# Meta description channels. No arguments. Result is the +# Meta description handle of the new channel. +# Meta platform tcl +# Meta require TclOO +# Meta require tcl::chan::events +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +package require TclOO +package require tcl::chan::events + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::chan {} + +proc ::tcl::chan::zero {} { + return [::chan create {read} [zero::implementation new]] +} + +oo::class create ::tcl::chan::zero::implementation { + superclass tcl::chan::events ; # -> initialize, finalize, watch + + method initialize {args} { + my allow read + next {*}$args + } + + # Generate and return a block of N null bytes, as requested. + # Zero device. + + method read {c n} { + return [binary format @$n] + } +} + +# # ## ### ##### ######## ############# +package provide tcl::chan::zero 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_core/core.tcl b/src/bootsupport/lib/virtchannel_core/core.tcl new file mode 100644 index 00000000..a54892ef --- /dev/null +++ b/src/bootsupport/lib/virtchannel_core/core.tcl @@ -0,0 +1,75 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::core 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Support package handling a core +# Meta description aspect of reflected base channels +# Meta description (initialization, finalization). +# Meta description It is expected that this class +# Meta description is used as either one superclass of the +# Meta description class C for a specific channel, or is +# Meta description mixed into C. +# Meta platform tcl +# Meta require TclOO +# Meta require {Tcl 8.5} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +if {[catch {package require tcl::oo}]} { + package require TclOO +} + +# # ## ### ##### ######## ############# + +oo::class create ::tcl::chan::core { + destructor { + if {$channel eq {}} return + close $channel + return + } + + # # ## ### ##### ######## ############# + + method initialize {thechannel mode} { + set methods [info object methods [self] -all] + + # Note: Checking of the mode against the supported methods is + # done by the caller. + + set channel $thechannel + set supported {} + foreach m { + initialize finalize watch read write seek configure cget + cgetall blocking + } { + if {$m in $methods} { + lappend supported $m + } + } + return $supported + } + + method finalize {c} { + set channel {} ; # Prevent destroctor from calling close. + my destroy + return + } + + # # ## ### ##### ######## ############# + + variable channel + + # channel The channel the handler belongs to. + # # ## ### ##### ######## ############# +} + +# # ## ### ##### +package provide tcl::chan::core 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_core/events.tcl b/src/bootsupport/lib/virtchannel_core/events.tcl new file mode 100644 index 00000000..6a7efc57 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_core/events.tcl @@ -0,0 +1,156 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::chan::events 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Support package handling a core +# Meta description aspect of reflected base channels +# Meta description (timer +# Meta description driven file event support). Controls a +# Meta description timer generating the expected read/write +# Meta description events. It is expected that this class +# Meta description is used as either one superclass of the +# Meta description class C for a specific channel, or is +# Meta description mixed into C. +# Meta platform tcl +# Meta require tcl::chan::core +# Meta require TclOO +# Meta require {Tcl 8.5} +# @@ Meta End + +# TODO :: set/get accessor methods for the timer delay + +# # ## ### ##### ######## ############# + +package require Tcl 8.5 9 +if {[catch {package require tcl::oo}]} { + package require TclOO +} +package require tcl::chan::core + +# # ## ### ##### ######## ############# + +oo::class create ::tcl::chan::events { + superclass ::tcl::chan::core ; # -> initialize, finalize, destructor + + constructor {} { + array set allowed { + read 0 + write 0 + } + set requested {} + set delay 10 + return + } + + # # ## ### ##### ######## ############# + + method finalize {c} { + my disallow read write + next $c + } + + # Allow/disallow the posting of events based on the + # events requested by Tcl's IO system, and the mask of + # events the instance's channel can handle, per all + # preceding calls of allow and disallow. + + method watch {c requestmask} { + if {$requestmask eq $requested} return + set requested $requestmask + my Update + return + } + + # # ## ### ##### ######## ############# + + # Declare that the named events are handled by the + # channel. This may start a timer to periodically post + # these events to the instance's channel. + + method allow {args} { + my Allowance $args yes + return + } + + # Declare that the named events are not handled by the + # channel. This may stop the periodic posting of events + # to the instance's channel. + + method disallow {args} { + my Allowance $args no + return + } + + # # ## ### ##### ######## ############# + + # Event System State - Timer driven + + variable timer allowed requested posting delay + + # channel = The channel to post events to - provided by superclass + # timer = Timer controlling the posting. + # allowed = Set of events allowed to post. + # requested = Set of events requested by core. + # posting = Set of events we are posting. + # delay = Millisec interval between posts. + + # 'allowed' is an Array (event name -> boolean). The + # value is true if the named event is allowed to be + # posted. + + # Common code used by both allow and disallow to enter + # the state change. + + method Allowance {events enable} { + set changed no + foreach event $events { + if {$allowed($event) == $enable} continue + set allowed($event) $enable + set changed yes + } + if {!$changed} return + my Update + return + } + + # Merge the current event allowance and the set of + # requested events into one datum, the set of events to + # post. From that then derive whether we need a timer or + # not and act accordingly. + + method Update {} { + catch { after cancel $timer } + set posting {} + foreach event $requested { + if {!$allowed($event)} continue + lappend posting $event + } + if {[llength $posting]} { + set timer [after $delay \ + [namespace code [list my Post]]] + } else { + catch { unset timer } + } + return + } + + # Post the current set of events, then reschedule to + # make this periodic. + + method Post {} { + my variable channel + set timer [after $delay \ + [namespace code [list my Post]]] + chan postevent $channel $posting + return + } +} + +# # ## ### ##### +package provide tcl::chan::events 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_core/pkgIndex.tcl b/src/bootsupport/lib/virtchannel_core/pkgIndex.tcl new file mode 100644 index 00000000..300eb278 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_core/pkgIndex.tcl @@ -0,0 +1,8 @@ +if {![package vsatisfies [package provide Tcl] 8.5 9]} {return} + +package ifneeded tcl::chan::core 1.1 [list source [file join $dir core.tcl]] +package ifneeded tcl::chan::events 1.1 [list source [file join $dir events.tcl]] + +if {![package vsatisfies [package provide Tcl] 8.6 9]} {return} + +package ifneeded tcl::transform::core 1.1 [list source [file join $dir transformcore.tcl]] diff --git a/src/bootsupport/lib/virtchannel_core/transformcore.tcl b/src/bootsupport/lib/virtchannel_core/transformcore.tcl new file mode 100644 index 00000000..3cd8c696 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_core/transformcore.tcl @@ -0,0 +1,71 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::core 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Support package handling a core +# Meta description aspect of reflected transform channels +# Meta description (initialization, finalization). +# Meta description It is expected that this class +# Meta description is used as either one superclass of the +# Meta description class C for a specific channel, or is +# Meta description mixed into C. +# Meta platform tcl +# Meta require TclOO +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 + +# # ## ### ##### ######## ############# + +oo::class create ::tcl::transform::core { + destructor { + if {$channel eq {}} return + close $channel + return + } + + # # ## ### ##### ######## ############# + + method initialize {thechannel mode} { + set methods [info object methods [self] -all] + + # Note: Checking of the mode against the supported methods is + # done by the caller. + + set channel $thechannel + set supported {} + foreach m { + initialize finalize read write drain flush limit? + } { + if {$m in $methods} { + lappend supported $m + } + } + return $supported + } + + method finalize {c} { + set channel {} ; # Prevent destroctor from calling close. + my destroy + return + } + + # # ## ### ##### ######## ############# + + variable channel + + # channel The channel the handler belongs to. + # # ## ### ##### ######## ############# +} + +# # ## ### ##### +package provide tcl::transform::core 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/adler32.tcl b/src/bootsupport/lib/virtchannel_transform/adler32.tcl new file mode 100644 index 00000000..af31e0cc --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/adler32.tcl @@ -0,0 +1,103 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::adler32 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::notes For other observers see crc32, counter, +# Meta as::notes identity, and observer (stream copy). +# Meta description Implementation of an adler32 checksum +# Meta description transformation. Based on Tcl 8.6's +# Meta description transformation reflection support (TIP +# Meta description 230), and its zlib support (TIP 234) for +# Meta description the adler32 functionality. An observer +# Meta description instead of a transformation. For details +# Meta description on the adler checksum see +# Meta description http://en.wikipedia.org/wiki/Adler-32 . +# Meta description The observer saves the checksums into two +# Meta description namespaced external variables specified +# Meta description at construction time. Exports a single +# Meta description command adding a new transformation of +# Meta description this type to a channel. One argument, +# Meta description the channel to extend, plus options to +# Meta description specify the variables for the checksums. +# Meta description No result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::adler32 {chan args} { + ::chan push $chan [adler32::implementation new {*}$args] +} + +oo::class create ::tcl::transform::adler32::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + # This transformation continuously computes a checksum from the + # data it sees. This data may be arbitrary parts of the input or + # output if the channel is seeked while the transform is + # active. This may not be what is wanted and the desired behaviour + # may require the destruction of the transform before seeking. + + method write {c data} { + my Adler32 -write-variable $data + return $data + } + + method read {c data} { + my Adler32 -read-variable $data + return $data + } + + # # ## ### ##### ######## ############# + + constructor {args} { + array set options { + -read-variable {} + -write-variable {} + } + # todo: validity checking of options (legal names, legal + # values, etc.) + array set options $args + my Init -read-variable + my Init -write-variable + return + } + + # # ## ### ##### ######## ############# + + variable options + + # # ## ### ##### ######## ############# + + method Init {o} { + if {$options($o) eq ""} return + upvar #0 $options($o) adler + set adler 1 + return + } + + method Adler32 {o data} { + if {$options($o) eq ""} return + upvar #0 $options($o) adler + set adler [zlib adler32 $data $adler] + return + } +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::adler32 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/base64.tcl b/src/bootsupport/lib/virtchannel_transform/base64.tcl new file mode 100644 index 00000000..feaf7da1 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/base64.tcl @@ -0,0 +1,111 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::base64 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::notes Possibilities for extension: Currently +# Meta as::notes the mapping between read/write and +# Meta as::notes decode/encode is fixed. Allow it to be +# Meta as::notes configured at construction time. +# Meta description Implementation of a base64 +# Meta description transformation (RFC 4648). Based on Tcl +# Meta description 8.6's transformation reflection support +# Meta description (TIP 230) and binary en/decode (TIP 317). +# Meta description Exports a single command adding a new +# Meta description transformation of this type to a channel. +# Meta description One argument, the channel to extend. No +# Meta description result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::base64 {chan} { + ::chan push $chan [base64::implementation new] + return +} + +oo::class create ::tcl::transform::base64::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + method write {c data} { + my Code encodebuf encode $data 3 + } + + method read {c data} { + my Code decodebuf decode $data 4 + } + + method flush {c} { + set data [binary encode base64 $encodebuf] + set encodebuf {} + return $data + } + + method drain {c} { + set data [binary decode base64 $decodebuf] + set decodebuf {} + return $data + } + + method clear {c} { + set decodebuf {} + return + } + + # # ## ### ##### ######## ############# + + constructor {} { + set encodebuf {} + set decodebuf {} + return + } + + # # ## ### ##### ######## ############# + + variable encodebuf decodebuf + + # # ## ### ##### ######## ############# + + method Code {bufvar op data n} { + upvar 1 $bufvar buffer + + append buffer $data + + set n [my Complete $buffer $n] + if {$n < 0} { + return {} + } + + set result \ + [binary $op base64 \ + [string range $buffer 0 $n]] + incr n + set buffer \ + [string range $buffer $n end] + + return $result + } + + method Complete {buffer n} { + set len [string length $buffer] + return [expr {(($len / $n) * $n)-1}] + } +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::base64 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/counter.tcl b/src/bootsupport/lib/virtchannel_transform/counter.tcl new file mode 100644 index 00000000..4b8a6f21 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/counter.tcl @@ -0,0 +1,94 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::counter 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::notes For other observers see adler32, crc32, +# Meta as::notes identity, and observer (stream copy). +# Meta as::notes Possibilities for extension: Separate +# Meta as::notes counters per byte value. Count over +# Meta as::notes fixed time-intervals = channel speed. +# Meta as::notes Use callbacks or traces to save changes +# Meta as::notes in the counters, etc. as time-series. +# Meta as::notes Compute statistics over the time-series. +# Meta description Implementation of a counter +# Meta description transformation. Based on Tcl 8.6's +# Meta description transformation reflection support (TIP +# Meta description 230). An observer instead of a +# Meta description transformation, it counts the number of +# Meta description bytes read and written. The observer +# Meta description saves the counts into two external +# Meta description namespaced variables specified at +# Meta description construction time. Exports a single +# Meta description command adding a new transformation of +# Meta description this type to a channel. One argument, +# Meta description the channel to extend, plus options to +# Meta description specify the variables for the counters. +# Meta description No result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::counter {chan args} { + ::chan push $chan [counter::implementation new {*}$args] +} + +oo::class create ::tcl::transform::counter::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + method write {c data} { + my Count -write-variable $data + return $data + } + + method read {c data} { + my Count -read-variable $data + return $data + } + + # No partial data, nor state => no flush, drain, nor clear needed. + + # # ## ### ##### ######## ############# + + constructor {args} { + array set options { + -read-variable {} + -write-variable {} + } + # todo: validity checking of options (legal names, legal + # values, etc.) + array set options $args + return + } + + # # ## ### ##### ######## ############# + + variable options + + # # ## ### ##### ######## ############# + + method Count {o data} { + if {$options($o) eq ""} return + upvar #0 $options($o) counter + incr counter [string length $data] + return + } +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::counter 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/crc32.tcl b/src/bootsupport/lib/virtchannel_transform/crc32.tcl new file mode 100644 index 00000000..28974910 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/crc32.tcl @@ -0,0 +1,103 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::crc32 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::notes For other observers see adler32, counter, +# Meta as::notes identity, and observer (stream copy). +# Meta description Implementation of a crc32 checksum +# Meta description transformation. Based on Tcl 8.6's +# Meta description transformation reflection support (TIP +# Meta description 230), and its zlib support (TIP 234) for +# Meta description the crc32 functionality. An observer +# Meta description instead of a transformation. For details +# Meta description on the crc checksum see +# Meta description http://en.wikipedia.org/wiki/Cyclic_redundancy_check#Commonly_used_and_standardised_CRCs . +# Meta description The observer saves the checksums into two +# Meta description namespaced external variables specified +# Meta description at construction time. Exports a single +# Meta description command adding a new transformation of +# Meta description this type to a channel. One argument, +# Meta description the channel to extend, plus options to +# Meta description specify the variables for the checksums. +# Meta description No result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::crc32 {chan args} { + ::chan push $chan [crc32::implementation new {*}$args] +} + +oo::class create ::tcl::transform::crc32::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + # This transformation continuously computes a checksum from the + # data it sees. This data may be arbitrary parts of the input or + # output if the channel is seeked while the transform is + # active. This may not be what is wanted and the desired behaviour + # may require the destruction of the transform before seeking. + + method write {c data} { + my Crc32 -write-variable $data + return $data + } + + method read {c data} { + my Crc32 -read-variable $data + return $data + } + + # # ## ### ##### ######## ############# + + constructor {args} { + array set options { + -read-variable {} + -write-variable {} + } + # todo: validity checking of options (legal names, legal + # values, etc.) + array set options $args + my Init -read-variable + my Init -write-variable + return + } + + # # ## ### ##### ######## ############# + + variable options + + # # ## ### ##### ######## ############# + + method Init {o} { + if {$options($o) eq ""} return + upvar #0 $options($o) crc + set crc 0 + return + } + + method Crc32 {o data} { + if {$options($o) eq ""} return + upvar #0 $options($o) crc + set crc [zlib crc32 $data $crc] + return + } +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::crc32 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/hex.tcl b/src/bootsupport/lib/virtchannel_transform/hex.tcl new file mode 100644 index 00000000..799eac76 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/hex.tcl @@ -0,0 +1,58 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::hex 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Implementation of a hex transformation, +# Meta description using Tcl 8.6's transformation +# Meta description reflection support. Uses the binary +# Meta description command to implement the transformation. +# Meta description Exports a single command adding a new +# Meta description transform of this type to a channel. One +# Meta description argument, the channel to extend. No +# Meta description result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::hex {chan} { + ::chan push $chan [hex::implementation new] + return +} + +oo::class create ::tcl::transform::hex::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + method write {c data} { + # bytes -> hex + binary scan $data H* hex + return $hex + } + + method read {c data} { + # hex -> bytes + return [binary format H* $data] + } + + # No partial data, nor state => no flush, drain, nor clear needed. + + # # ## ### ##### ######## ############# +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::hex 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/identity.tcl b/src/bootsupport/lib/virtchannel_transform/identity.tcl new file mode 100644 index 00000000..d3b613ce --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/identity.tcl @@ -0,0 +1,59 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::identity 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::notes The prototypical observer transformation. +# Meta as::notes To observers what null is to reflected +# Meta as::notes base channels. For other observers see +# Meta as::notes adler32, crc32, counter, and observer +# Meta as::notes (stream copy). +# Meta description Implementation of an identity +# Meta description transformation, i.e one which does not +# Meta description change the data in any way, shape, or +# Meta description form. Based on Tcl 8.6's transformation +# Meta description reflection support. Exports a single +# Meta description command adding a new transform of this +# Meta description type to a channel. One argument, the +# Meta description channel to extend. No result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::identity {chan} { + ::chan push $chan [identity::implementation new] +} + +oo::class create ::tcl::transform::identity::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + method write {c data} { + return $data + } + + method read {c data} { + return $data + } + + # No partial data, nor state => no flush, drain, nor clear needed. + + # # ## ### ##### ######## ############# +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::identity 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/limitsize.tcl b/src/bootsupport/lib/virtchannel_transform/limitsize.tcl new file mode 100644 index 00000000..7d1f821b --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/limitsize.tcl @@ -0,0 +1,88 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::limitsize 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::notes Possibilities for extension: Trigger the +# Meta as::notes EOF when finding specific patterns in +# Meta as::notes the input. Trigger the EOF based on some +# Meta as::notes external signal routed into the limiter. +# Meta as::notes Make the limit reconfigurable. +# Meta description Implementation of a transformation +# Meta description limiting the number of bytes read +# Meta description from its channel. An observer instead of +# Meta description a transformation, forcing an artificial +# Meta description EOF marker. Based on Tcl 8.6's +# Meta description transformation reflection support. +# Meta description Exports a single command adding a new +# Meta description transform of this type to a channel. One +# Meta description argument, the channel to extend, and the +# Meta description number of bytes to allowed to be read. +# Meta description No result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# This may help with things like zlib compression of messages. Have +# the message format a length at the front, followed by a payload of +# that size. Now we may compress messages. On the read side we can use +# the limiter to EOF on a message, then reset the limit for the +# next. This is a half-baked idea. + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::limitsize {chan max} { + ::chan push $chan [limitsize::implementation new $max] +} + +oo::class create ::tcl::transform::limitsize::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + method write {c data} { + return $data + } + + method read {c data} { + # Reduce the limit of bytes allowed in the future according to + # the number of bytes we have seen already. + + if {$max > 0} { + incr max -[string length $data] + if {$max < 0} { + set max 0 + } + } + return $data + } + + method limit? {c} { + return $max + } + + # # ## ### ##### ######## ############# + + constructor {themax} { + set max $themax + return + } + + variable max + + # # ## ### ##### ######## ############# +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::limitsize 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/observe.tcl b/src/bootsupport/lib/virtchannel_transform/observe.tcl new file mode 100644 index 00000000..93e1331e --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/observe.tcl @@ -0,0 +1,80 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::observe 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::notes For other observers see adler32, crc32, +# Meta as::notes identity, and counter. +# Meta as::notes Possibilities for extension: Save the +# Meta as::notes observed bytes to variables instead of +# Meta as::notes channels. Use callbacks to save the +# Meta as::notes observed bytes. +# Meta description Implementation of an observer +# Meta description transformation copying the bytes going +# Meta description through it into two channels configured +# Meta description at construction time. Based on Tcl 8.6's +# Meta description transformation reflection support. +# Meta description Exports a single command adding a new +# Meta description transformation of this type to a channel. +# Meta description Three arguments, the channel to extend, +# Meta description plus the channels to write the bytes to. +# Meta description No result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::observe {chan logw logr} { + ::chan push $chan [observe::implementation new $logw $logr] +} + +oo::class create ::tcl::transform::observe::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + method write {c data} { + if {$logw ne {}} { + puts -nonewline $logw $data + } + return $data + } + + method read {c data} { + if {$logr ne {}} { + puts -nonewline $logr $data + } + return $data + } + + # No partial data, nor state => no flush, drain, nor clear needed. + + # # ## ### ##### ######## ############# + + constructor {lw lr} { + set logr $lr + set logw $lw + return + } + + # # ## ### ##### ######## ############# + + variable logr logw + + # # ## ### ##### ######## ############# +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::observe 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/otp.tcl b/src/bootsupport/lib/virtchannel_transform/otp.tcl new file mode 100644 index 00000000..61663f73 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/otp.tcl @@ -0,0 +1,98 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::otp 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Implementation of an onetimepad +# Meta description encryption transformation. Based on Tcl +# Meta description 8.6's transformation reflection support. +# Meta description The key bytes are read from two channels +# Meta description configured at construction time. Exports +# Meta description a single command adding a new +# Meta description transformation of this type to a channel. +# Meta description Three arguments, the channel to extend, +# Meta description plus the channels to read the keys from. +# Meta description No result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::otp {chan keychanw keychanr} { + ::chan push $chan [otp::implementation new $keychanw $keychanr] +} + +oo::class create ::tcl::transform::otp::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + # This transformation is intended for streaming operation. Seeking + # the channel while it is active may cause undesirable + # output. Proper behaviour may require the destruction of the + # transform before seeking. + + method write {c data} { + return [my Xor $data $keychanw] + } + + method read {c data} { + return [my Xor $data $keychanr] + } + + # # ## ### ##### ######## ############# + + constructor {keyw keyr} { + set keychanr $keyr + set keychanw $keyw + return + } + + # # ## ### ##### ######## ############# + + variable keychanr keychanw + + # # ## ### ##### ######## ############# + + # A very convoluted way to perform the XOR would be to use TIP + # #317's hex encoding to convert the bytes into strings, then zip + # key and data into an interleaved string (nibble wise), then + # perform the xor as a 'string map' of the whole thing, and at + # last 'binary decode hex' the string back into bytes. Even so + # most ops would run on the whole message at C level. Except for + # the interleave. :( + + method Xor {data keychan} { + # xor is done byte-wise. to keep IO down we read the key bytes + # once, before the loop handling the bytes. Note that we are + # having binary data at this point, making it necessary to + # convert into numbers (scan), and back (binary format). + + set keys [read $keychan [string length $data]] + set result {} + foreach d [split $data {}] k [split $keys {}] { + append result \ + [binary format c \ + [expr { + [scan $d %c] ^ + [scan $k %c] + }]] + } + return $result + } +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::otp 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/pkgIndex.tcl b/src/bootsupport/lib/virtchannel_transform/pkgIndex.tcl new file mode 100644 index 00000000..0067c17e --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/pkgIndex.tcl @@ -0,0 +1,14 @@ +if {![package vsatisfies [package provide Tcl] 8.6 9]} {return} + +package ifneeded tcl::transform::adler32 1.1 [list source [file join $dir adler32.tcl]] +package ifneeded tcl::transform::base64 1.1 [list source [file join $dir base64.tcl]] +package ifneeded tcl::transform::counter 1.1 [list source [file join $dir counter.tcl]] +package ifneeded tcl::transform::crc32 1.1 [list source [file join $dir crc32.tcl]] +package ifneeded tcl::transform::hex 1.1 [list source [file join $dir hex.tcl]] +package ifneeded tcl::transform::identity 1.1 [list source [file join $dir identity.tcl]] +package ifneeded tcl::transform::limitsize 1.1 [list source [file join $dir limitsize.tcl]] +package ifneeded tcl::transform::observe 1.1 [list source [file join $dir observe.tcl]] +package ifneeded tcl::transform::otp 1.1 [list source [file join $dir otp.tcl]] +package ifneeded tcl::transform::rot 1.1 [list source [file join $dir rot.tcl]] +package ifneeded tcl::transform::spacer 1.1 [list source [file join $dir spacer.tcl]] +package ifneeded tcl::transform::zlib 1.0.2 [list source [file join $dir zlib.tcl]] diff --git a/src/bootsupport/lib/virtchannel_transform/rot.tcl b/src/bootsupport/lib/virtchannel_transform/rot.tcl new file mode 100644 index 00000000..2fa98034 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/rot.tcl @@ -0,0 +1,95 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::rot 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Implementation of a rot +# Meta description encryption transformation. Based on Tcl +# Meta description 8.6's transformation reflection support. +# Meta description The key byte is +# Meta description configured at construction time. Exports +# Meta description a single command adding a new +# Meta description transformation of this type to a channel. +# Meta description Two arguments, the channel to extend, +# Meta description plus the key byte. +# Meta description No result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::rot {chan key} { + ::chan push $chan [rot::implementation new $key] +} + +oo::class create ::tcl::transform::rot::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + # This transformation is intended for streaming operation. Seeking + # the channel while it is active may cause undesirable + # output. Proper behaviour may require the destruction of the + # transform before seeking. + + method write {c data} { + return [my Rot $data $key] + } + + method read {c data} { + return [my Rot $data $ikey] + } + + # # ## ### ##### ######## ############# + + constructor {thekey} { + set key [expr {$thekey % 26}] + set ikey [expr {26 - $key}] + return + } + + # # ## ### ##### ######## ############# + + variable key ikey + + # # ## ### ##### ######## ############# + + method Rot {data key} { + # rot'ation is done byte-wise. Note that we are having binary + # data at this point, making it necessary to convert into + # numbers (scan), and back (binary format). + + set result {} + foreach d [split $data {}] { + set dx [scan $d %c] + if {(65 <= $dx) && ($dx <= 90)} { + set n [binary format c \ + [expr { (($dx - 65 + $key) % 26) + 65 }]] + } elseif {(97 <= $dx) && ($dx <= 122)} { + set n [binary format c \ + [expr { (($dx - 97 + $key) % 26) + 97 }]] + } else { + set n $d + } + + append result $n + + } + return $result + } +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::rot 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/spacer.tcl b/src/bootsupport/lib/virtchannel_transform/spacer.tcl new file mode 100644 index 00000000..e3f481a5 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/spacer.tcl @@ -0,0 +1,151 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::spacer 1.1 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta description Implementation of a spacer +# Meta description transformation, using Tcl 8.6's +# Meta description transformation reflection support. Uses +# Meta description counters to implement the transformation, +# Meta description i.e. decide where to insert the spacing. +# Meta description Exports a single command adding a new +# Meta description transform of this type to a channel. One +# Meta description argument, the channel to extend. No +# Meta description result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::spacer {chan n {space { }}} { + ::chan push $chan [spacer::implementation new $n $space] + return +} + +oo::class create ::tcl::transform::spacer::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + # This transformation is intended for streaming operation. Seeking + # the channel while it is active may cause undesirable + # output. Proper behaviour may require the destruction of the + # transform before seeking. + + method write {c data} { + # add spacing, data is split into groups of delta chars. + set result {} + set len [string length $data] + + if {$woffset} { + # The beginning of the buffer is the remainder of the + # partial group found at the end of the buffer in the last + # call. It may still be partial, if the current buffer is + # short enough. + + if {($woffset + $len) < $delta} { + # Yes, the group is still not fully covered. + # Move the offset forward, and return the whole + # buffer. spacing is not needed yet. + incr woffset $len + return $data + } + + # The buffer completes the group. Add it and the following + # spacing, then fix the offset to start the processing of + # the groups coming after at the proper location. + + set stop [expr {$delta - $woffset - 1}] + + append result [string range $data 0 $stop] + append result $spacing + + set woffset $stop + incr woffset + } + + # Process full groups in the middle of the incoming buffer. + + set at $woffset + set stop [expr {$at + $delta - 1}] + while {$stop < $len} { + append result [string range $data $at $stop] + append result $spacing + incr at $delta + incr stop $delta + } + + # Process partial group at the end of the buffer and remember + # the offset, for the processing of the group remainder in the + # next call. + + if {($at < $len) && ($stop >= $len)} { + append result [string range $data $at end] + } + set woffset [expr {$len - $at}] + return $result + } + + method read {c data} { + # remove spacing from groups of delta+sdelta chars, keeping + # the first delta in each group. + set result {} + set iter [expr {$delta + $sdelta}] + set at 0 + if {$roffset} { + if {$roffset < $delta} { + append result [string range $data 0 ${roffset}-1] + } + incr at [expr {$iter - $roffset}] + } + set len [string length $data] + set end [expr {$at + $delta - 1}] + set stop [expr {$at + $iter - 1}] + while {$stop < $len} { + append result [string range $data $at $end] + incr at $iter + incr end $iter + incr stop $iter + } + if {$end < $len} { + append result [string range $data $at $end] + set roffset [expr {$len - $end + 1}] + } elseif {$at < $len} { + append result [string range $data $at end] + set roffset [expr {$len - $at}] + } + return [list $result $roffset] + } + + # # ## ### ##### ######## ############# + + constructor {n space} { + set roffset 0 + set woffset 0 + set delta $n + set spacing $space + set sdelta [string length $spacing] + return + } + + # # ## ### ##### ######## ############# + + variable roffset woffset delta spacing sdelta + + # # ## ### ##### ######## ############# +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::spacer 1.1 +return diff --git a/src/bootsupport/lib/virtchannel_transform/zlib.tcl b/src/bootsupport/lib/virtchannel_transform/zlib.tcl new file mode 100644 index 00000000..8599f248 --- /dev/null +++ b/src/bootsupport/lib/virtchannel_transform/zlib.tcl @@ -0,0 +1,100 @@ +# -*- tcl -*- +# # ## ### ##### ######## ############# +# (C) 2009 Andreas Kupries + +# @@ Meta Begin +# Package tcl::transform::zlib 1.0.2 +# Meta as::author {Andreas Kupries} +# Meta as::copyright 2009 +# Meta as::license BSD +# Meta as::notes Possibilities for extension: Currently +# Meta as::notes the mapping between read/write and +# Meta as::notes de/compression is fixed. Allow it to be +# Meta as::notes configured at construction time. +# Meta description Implementation of a zlib (de)compressor. +# Meta description Based on Tcl 8.6's transformation +# Meta description reflection support (TIP 230) and zlib +# Meta description support (TIP 234). Compresses on write. +# Meta description Exports a single command adding a new +# Meta description transformation of this type to a channel. +# Meta description Two arguments, the channel to extend, +# Meta description and the compression level. No result. +# Meta platform tcl +# Meta require tcl::transform::core +# Meta require {Tcl 8.6} +# @@ Meta End + +# # ## ### ##### ######## ############# + +package require Tcl 8.6 9 +package require tcl::transform::core + +# # ## ### ##### ######## ############# + +namespace eval ::tcl::transform {} + +proc ::tcl::transform::zlib {chan {level 4}} { + ::chan push $chan [zlib::implementation new $level] + return +} + +oo::class create ::tcl::transform::zlib::implementation { + superclass tcl::transform::core ;# -> initialize, finalize, destructor + + # This transformation is intended for streaming operation. Seeking + # the channel while it is active may cause undesirable + # output. Proper behaviour may require the destruction of the + # transform before seeking. + + method initialize {c mode} { + set compressor [zlib stream deflate -level $level] + set decompressor [zlib stream inflate] + + next $c $mode + } + + method finalize {c} { + $compressor close + $decompressor close + + next $c + } + + method write {c data} { + $compressor put $data + return [$compressor get] + } + + method read {c data} { + $decompressor put $data + return [$decompressor get] + } + + method flush {c} { + $compressor flush + return [$compressor get] + } + + method drain {c} { + $decompressor flush + return [$decompressor get] + } + + # # ## ### ##### ######## ############# + + constructor {thelevel} { + # Should validate input (level in (0 ...9)) + set level $thelevel + return + } + + # # ## ### ##### ######## ############# + + variable level compressor decompressor + + # # ## ### ##### ######## ############# +} + +# # ## ### ##### ######## ############# +package provide tcl::transform::zlib 1.0.2 +return diff --git a/src/bootsupport/modules/include_modules.config b/src/bootsupport/modules/include_modules.config index 2000d2f0..edd7393d 100644 --- a/src/bootsupport/modules/include_modules.config +++ b/src/bootsupport/modules/include_modules.config @@ -99,5 +99,6 @@ set bootsupport_modules [list\ modules natsort\ modules oolib\ modules zipper\ + modules zzzload\ ] diff --git a/src/bootsupport/modules/zzzload-0.1.0.tm b/src/bootsupport/modules/zzzload-0.1.0.tm new file mode 100644 index 00000000..def41578 --- /dev/null +++ b/src/bootsupport/modules/zzzload-0.1.0.tm @@ -0,0 +1,131 @@ +# -*- tcl -*- +# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from -buildversion.txt +# +# 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) 2023 +# +# @@ Meta Begin +# Application zzzload 0.1.0 +# Meta platform tcl +# Meta license BSD +# @@ Meta End + + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +## Requirements +##e.g package require frobz + +package require Thread + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +namespace eval zzzload { + variable loader_tid "" ;#thread id + proc stacktrace {} { + set stack "Stack trace:\n" + for {set i 1} {$i < [info level]} {incr i} { + set lvl [info level -$i] + set pname [lindex $lvl 0] + append stack [string repeat " " $i]$pname + + if {![catch {info args $pname} pargs]} { + foreach value [lrange $lvl 1 end] arg $pargs { + + if {$value eq ""} { + if {$arg != 0} { + info default $pname $arg value + } + } + append stack " $arg='$value'" + } + } else { + append stack " !unknown vars for $pname" + } + + append stack \n + } + return $stack + } + proc pkg_require {pkgname args} { + variable loader_tid + if {[set ver [package provide twapi]] ne ""} { + #skip the whole shebazzle if it's already loaded + return $ver + } + if {$loader_tid eq ""} { + set loader_tid [thread::create -joinable -preserved] + } + if {![tsv::exists zzzload_pkg $pkgname]} { + #puts stderr "zzzload pkg_require $pkgname" + #puts [stacktrace] + tsv::set zzzload_pkg $pkgname "loading" + tsv::set zzzload_pkg_mutex $pkgname [thread::mutex create] + set cond [thread::cond create] + tsv::set zzzload_pkg_cond $pkgname $cond + thread::send -async $loader_tid [string map [list $pkgname $cond] { + if {![catch {package require } returnver]} { + tsv::set zzzload_pkg $returnver + } else { + tsv::set zzzload_pkg "failed" + } + thread::cond notify + }] + return "loading" + } else { + return [tsv::get zzzload_pkg $pkgname] + } + } + proc pkg_wait {pkgname} { + if {[set ver [package provide twapi]] ne ""} { + return $ver + } + + set pkgstate [tsv::get zzzload_pkg $pkgname] + if {$pkgstate eq "loading"} { + set mutex [tsv::get zzzload_pkg_mutex $pkgname] + thread::mutex lock $mutex + set cond [tsv::get zzzload_pkg_cond $pkgname] + while {[tsv::get zzzload_pkg $pkgname] eq "loading"} { + thread::cond wait $cond $mutex 3000 + } + set result [tsv::get zzzload_pkg $pkgname] + thread::mutex unlock $mutex + return $result + } else { + return $pkgstate + } + } + proc shutdown {} { + variable loader_tid + if {[thread::exists $loader_tid]} { + thread::release $loader_tid + thread::join $loader_tid + set loader_tid "" + } + } + +} + + + + + + + + + + + + + + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +## Ready +package provide zzzload [namespace eval zzzload { + variable version + set version 0.1.0 +}] +return \ No newline at end of file diff --git a/src/modules/punk/mix/templates/utility/scriptappwrappers/multishell.cmd b/src/modules/punk/mix/templates/utility/scriptappwrappers/multishell.cmd index 82d174a5..392d400f 100644 --- a/src/modules/punk/mix/templates/utility/scriptappwrappers/multishell.cmd +++ b/src/modules/punk/mix/templates/utility/scriptappwrappers/multishell.cmd @@ -752,12 +752,14 @@ function GetDynamicParamDictionary { return $DynParamDictionary } } +# Example usage: # GetDynamicParamDictionary # - This can make it easier to share a single set of param definitions between functions # - sample usage #function ParameterDefinitions { # param( -# [Parameter(Mandatory)][string] $myargument +# [Parameter(Mandatory)][string] $myargument, +# [Parameter(ValueFromRemainingArguments)] $opts # ) #} #function psmain { @@ -768,10 +770,15 @@ function GetDynamicParamDictionary { # #called once with $PSBoundParameters dictionary # #can be used to validate arguments, or set a simpler variable name for access # switch ($PSBoundParameters.keys) { -# 'myargumentname' { +# 'myargument' { # Set-Variable -Name $_ -Value $PSBoundParameters."$_" # } -# #... +# 'opts' { +# write-warning "Unused parameters: $($PSBoundParameters.$_)" +# } +# Default { +# write-warning "Unhandled parameter -> [$($_)]" +# } # } # foreach ($boundparam in $PSBoundParameters.GetEnumerator()) { # #... @@ -779,7 +786,7 @@ function GetDynamicParamDictionary { # } # end { # #Main function logic -# Write-Host "myargumentname value is: $myargumentname" +# Write-Host "myargument value is: $myargument" # #myotherfunction @PSBoundParameters # } #} diff --git a/src/runtime/mapvfs.config b/src/runtime/mapvfs.config index a0766222..ed09bcde 100644 --- a/src/runtime/mapvfs.config +++ b/src/runtime/mapvfs.config @@ -83,5 +83,6 @@ tclsh902z.exe {punk9win_for_tkruntime.vfs punk902z zip} #temp hack - todo fix .exe for x-platform #linux tclsh90 (zip) built with zig.build x-compile on windows -#tclsh90linux.exe {punk9linux.vfs punk90linux zip} +#tclsh90linux.exe {punk9linux.vfs punk90linux zip} +tclkit-902-Linux64-intel-dyn {punk9linux.vfs punk902linux-x86_64 zip} diff --git a/src/scriptapps/runtime.bash b/src/scriptapps/runtime.bash new file mode 100644 index 00000000..584a949f --- /dev/null +++ b/src/scriptapps/runtime.bash @@ -0,0 +1,111 @@ + +wdir="$(pwd)"; [ "$(pwd)" = "/" ] && wdir="" +case "$0" in + /*) scriptpath="${0}";; + *) scriptpath="$wdir/${0#./}";; +esac +scriptdir="${scriptpath%/*}" +scriptdir=$(realpath $scriptdir) +scriptpath=$(realpath $scriptpath) +basename=$(basename "$scriptpath") #e.g fetchruntime.bash +scriptroot="${basename%.*}" #e.g "fetchruntime" + +url_kitbase="https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" +runtime_available=0 +if [[ "$OSTYPE" == "linux"* ]]; then + arch=$(uname -i) + if [[ "$arch" == "x86_64"* ]]; then + url="${url_kitbase}/linux-x86_64/tclkit-902-Linux64-intel-dyn" + archdir="${scriptdir}/runtime/linux-x86_64" + output="${archdir}/tclkit-902-Linux64-intel-dyn" + runtime_available=1 + elif [[ "$arch" == "arm"* ]]; then + url="${url_kitbase}/linux-arm/tclkit-902-Linux64-arm-dyn" + archdir="${scriptdir}/runtime/linux-arm" + output="${archdir}/tclkit-902-Linux64-arm-dyn" + runtime_available=1 + fi + if [[ "$runtime_available" -eq 1 ]]; then + echo "Please ensure libxFt.so.2 is available" + echo "e.g on Ubuntu: sudo apt-get install libxft2" + fi + os="linux" +elif [[ "$OSTYPE" == "darwin"* ]]; then + os="macosx" + #assumed to be Mach-O 'universal binaries' for both x86-64 and arm? - REVIEW + url="${url_kitbase}/macosx/tclkit-902-Darwin64-dyn" + archdir="${scriptdir}/runtime/macosx/" + output="${archdir}/tclkit-902-Darwin64-dyn" + runtime_available=1 +elif [[ "$OSTYPE" == "freebsd"* ]]; then + os="freebsd" +elif [[ "$OSTYPE" == "dragonflybsd"* ]]; then + os="dragonflybsd" +elif [[ "$OSTYPE" == "netbsd"* ]]; then + os="netbsd" +elif [[ "$OSTYPE" == "win32" ]]; then + os="win32" + url="${url_kitbase}/win32-x86_64/tclsh902z.exe" + archdir="${scriptdir}/runtime/win32-x86_64/" + output="${archdir}/tcsh902z.exe" + runtime_available=1 +elif [[ "$OSTYPE" == "msys" ]]; then + echo MSYS + os="win32" + #use 'command -v' (shell builtin preferred over external which) + interp = `ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()' | sed -E 's/^.*\/|^-//'` + shellpath=`command -v $interp` + shellfolder="${shellpath%/*}" #avoid dependency on basename or dirname + #"c:/windows/system32/" is quite likely in the path ahead of msys,git etc. + #This breaks calls to various unix utils such as sed etc (wsl related?) + export PATH="$shellfolder${PATH:+:${PATH}}" + url="${url_kitbase}/win32-x86_64/tclsh902z.exe" + archdir="${scriptdir}/runtime/win32-x86_64" + output="${archdir}/tclsh902z.exe" + runtime_available=1 +else + #os="$OSTYPE" + os="other" +fi + +case "$1" in + "fetch") + + if [[ "$runtime_available" -eq 1 ]]; then + #test win32 + mkdir -p $archdir + echo "Attempting to download $url" + #wget $url -O $output + curl -SL --output "$output" "$url" + if [[ $? -eq 0 ]]; then + echo "File downloaded to $output" + chmod +x $output + else + echo "Error: Failed to download to $output" + fi + else + echo "No runtime currently available for $os" + fi + ;; + "list") + if [ -d $archdir ]; then + echo "$(ls $archdir -1 | wc -l) files in $archdir" + echo $(ls $archdir -1) + else + echo "No runtimes available in $archdir\n Use '$0 fetch' to install." + fi + ;; + "run") + #todo - lookup active runtime for os-arch from .toml file + activeruntime=$(ls $archdir -1 | tail -n 1) + activeruntime_fullpath="$archdir/$activeruntime" + echo "using $activeruntime_fullpath" + shift + echo "args: $@" + $activeruntime_fullpath "$@" + ;; + *) + echo "Usage: $0 {fetch|list|run}" + exit 1 + ;; +esac diff --git a/src/scriptapps/runtime.ps1 b/src/scriptapps/runtime.ps1 new file mode 100644 index 00000000..f6cab3b8 --- /dev/null +++ b/src/scriptapps/runtime.ps1 @@ -0,0 +1,184 @@ + + +function GetDynamicParamDictionary { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true, Mandatory=$true)] + [string] $CommandName + ) + + begin { + # Get a list of params that should be ignored (they're common to all advanced functions) + $CommonParameterNames = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject([type] [System.Management.Automation.Internal.CommonParameters]) | + Get-Member -MemberType Properties | + Select-Object -ExpandProperty Name + } + + process { + # Create the dictionary that this scriptblock will return: + $DynParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary + + # Convert to object array and get rid of Common params: + (Get-Command $CommandName | Select-Object -exp Parameters).GetEnumerator() | + Where-Object { $CommonParameterNames -notcontains $_.Key } | + ForEach-Object { + $DynamicParameter = New-Object System.Management.Automation.RuntimeDefinedParameter ( + $_.Key, + $_.Value.ParameterType, + $_.Value.Attributes + ) + $DynParamDictionary.Add($_.Key, $DynamicParameter) + } + + # Return the dynamic parameters + return $DynParamDictionary + } +} +function ParameterDefinitions { + param( + [Parameter(ValueFromRemainingArguments=$true)] $opts + ) +} + +function psmain { + [CmdletBinding()] + #Empty param block (extra params can be added) + param( + [Parameter(Mandatory=$false)][string] $action + ) + dynamicparam { + if ($action -eq 'list') { + } elseif ($action -eq 'fetch') { + #GetDynamicParamDictionary ParameterDefinitions + $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "fetchruntime" + Mandatory = $false + } + $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $attributeCollection.Add($parameterAttribute) + + $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'runtime', [string], $attributeCollection + ) + + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() + $paramDictionary.Add('runtime', $dynParam1) + return $paramDictionary + } elseif ($action -eq 'run') { + GetDynamicParamDictionary ParameterDefinitions + } else { + } + } + process { + #Called once - we get a single item being our PSBoundParameters dictionary + #write-host "Bound Parameters:$($PSBoundParameters.Keys)" + switch ($PSBoundParameters.keys) { + 'action' { + #write-host "got action " $PSBoundParameters.action + Set-Variable -Name $_ -Value $PSBoundParameters."$_" + $known_actions = @("fetch", "list", "run") + if (-not($known_actions -contains $action)) { + write-host "fetch '$action' not understood. Known_actions: $known_actions" + exit 1 + } + } + 'opts' { + #write-warning "Unused parameters: $($PSBoundParameters.$_)" + } + Default { + #write-warning "Unhandled parameter -> [$($_)]" + } + } + #foreach ($boundparam in $PSBoundParameters.Keys) { + # write-host "k: $boundparam" + #} + } + end { + # PSBoundParameters + #write-host "action:'$action'" + $outbase = $PSScriptRoot + $outbase = Resolve-Path -Path $outbase + #expected script location is the bin folder of a punk project + $rtfolder = Join-Path -Path $outbase -ChildPath "runtime" + $archfolder = Join-Path -Path $rtfolder -ChildPath "win32-x86_64" + switch ($action) { + 'fetch' { + $runtime = "tclsh902z.exe" + $archurl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master/win32-x86_64" + foreach ($boundparam in $PSBoundParameters.Keys) { + write-host "fetchopt: $boundparam $($PSBoundParameters[$boundparam])" + } + if ( $PSBoundParameters["runtime"].Length ) { + $runtime = $PSBoundParameters["runtime"] + } + $fileurl = "$archurl/$runtime" + $output = join-path $archfolder $runtime + + $container = split-path -Path $output -Parent + new-item -Path $container -ItemType Directory -force #create with intermediary folders if not already present + + if (-not(Test-Path -Path $output -PathType Leaf)) { + Write-Host "Downloading from $fileurl ..." + try { + $response = Invoke-WebRequest -Uri $fileurl -OutFile $output -ErrorAction Stop + Write-Host "Runtime saved at $output" + } + catch { + Write-Host "An error occurred: $($_.Exception.Message)" + if ($_.Exception.Response) { + Write-Host "HTTP Status code: $($_.Exception.Response.StatusCode)" + } + } + } else { + Write-Host "Runtime already found at $output" + } + } + 'run' { + #select first (or configured default) runtime and launch, passing arguments + if (-not(Test-Path -Path $archfolder -PathType Container)) { + write-host "No runtimes seem to be installed for win32-x86_64`nPlease use 'runtime.cmd fetch' to install" + } else { + $dircontents = (get-childItem -Path $archfolder -File | Sort-Object Name) + if ($dircontents.Count -gt 0) { + #write-host "run.." + #write-host "num params: $($PSBoundParameters.opts.count)" + #foreach ($boundparam in $PSBoundParameters.opts) { + # write-host $boundparam + #} + #todo - use 'active' runtime - need to lookup (PSToml?) + #when no 'active' runtime for this os-arch - use last item (sorted in dictionary order) + $active = $dircontents[-1] + #write-host "using: $active" + Start-Process -FilePath $active -ArgumentList $PSBoundParameters.opts -NoNewWindow -Wait + } else { + write-host "No files found in $archfolder" + write-host "No runtimes seem to be installed for win32-x86_64`nPlease use 'runtime.cmd fetch' to install." + } + } + } + 'list' { + if (test-path -Path $archfolder -Type Container) { + $dircontents = (get-childItem -Path $archfolder -File) + write-host "$(${dircontents}.count) files in $archfolder" + foreach ($f in $dircontents) { + write-host $f.Name + } + } else { + write-host "No runtimes seem to be installed for win32-x86_64 in $archfolder`nPlase use 'runtime.cmd fetch' to install." + } + } + default { + $actions = @("fetch", "list", "run") + write-host "Available actions: $actions" + } + } + + return $PSBoundParameters + } +} +#write-host (psmain @args) +$returnvalue = psmain @args +#Write-Host "Function Returned $returnvalue" -ForegroundColor Cyan +return $returnvalue +exit 0 + diff --git a/src/scriptapps/runtime_wrap.toml b/src/scriptapps/runtime_wrap.toml new file mode 100644 index 00000000..c9840a08 --- /dev/null +++ b/src/scriptapps/runtime_wrap.toml @@ -0,0 +1,20 @@ + +[application] + template="punk.multishell.cmd" + as_admin=false + + scripts=[ + "runtime.ps1", + "runtime.bash" + ] + + default_outputfile="runtime.cmd" + default_nextshellpath="/usr/bin/env bash" + default_nextshelltype="bash" + + #valid nextshelltype entries are: tcl perl powershell bash. + #nextshellpath entries must be 64 characters or less. + + win32.nextshellpath="powershell" + win32.nextshelltype="powershell" + win32.outputfile="runtime.cmd" diff --git a/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/libtcl9thread3.0.2.so b/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/libtcl9thread3.0.2.so new file mode 100644 index 0000000000000000000000000000000000000000..942beddfe2eec726e39d540dfe29e175ae5035f4 GIT binary patch literal 90483 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 Tvx4VO8G+Li7C=c~@T>;_VP*8s literal 0 HcmV?d00001 diff --git a/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/pkgIndex.tcl b/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/pkgIndex.tcl new file mode 100644 index 00000000..a6a7a228 --- /dev/null +++ b/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/pkgIndex.tcl @@ -0,0 +1,55 @@ +# -*- tcl -*- +# Tcl package index file, version 1.1 +# + +# Tcl 8.7 interps are only supported on 32-bit platforms. +# Lower than that is never supported. Bye! +if {![package vsatisfies [package provide Tcl] 9.0] + && ((![package vsatisfies [package provide Tcl] 8.7]) + || ($::tcl_platform(pointerSize)!=4))} { + return +} + +# All Tcl 8.7+ interps can [load] thread 3.0.2 +# +# For interps that are not thread-enabled, we still call [package ifneeded]. +# This is contrary to the usual convention, but is a good idea because we +# cannot imagine any other version of thread that might succeed in a +# thread-disabled interp. There's nothing to gain by yielding to other +# competing callers of [package ifneeded Thread]. On the other hand, +# deferring the error has the advantage that a script calling +# [package require Thread] in a thread-disabled interp gets an error message +# about a thread-disabled interp, instead of the message +# "can't find package thread". + +package ifneeded [string tolower thread] 3.0.2 \ + [list load [file join $dir libtcl9thread3.0.2.so] [string totitle thread]] +package ifneeded [string totitle thread] 3.0.2 \ + [list package require -exact [string tolower thread] 3.0.2] + +# package ttrace uses some support machinery. + +# In Tcl 8.7+ interps; use [::apply] + +package ifneeded ttrace 3.0.2 [list ::apply {{dir} { + if {[info exists ::env(TCL_THREAD_LIBRARY)] && + [file readable $::env(TCL_THREAD_LIBRARY)/ttrace.tcl]} { + source $::env(TCL_THREAD_LIBRARY)/ttrace.tcl + } elseif {[file readable [file join $dir .. lib ttrace.tcl]]} { + source [file join $dir .. lib ttrace.tcl] + } elseif {[file readable [file join $dir ttrace.tcl]]} { + source [file join $dir ttrace.tcl] + } elseif {[file exists //zipfs:/lib/thread/ttrace.tcl] || + ![catch {zipfs mount [file join $dir libtcl9thread3.0.2.so] //zipfs:/lib/thread}]} { + source //zipfs:/lib/thread/ttrace.tcl + } + if {[namespace which ::ttrace::update] ne ""} { + ::ttrace::update + } +}} $dir] +package ifneeded Ttrace 3.0.2 \ + [list package require -exact ttrace 3.0.2] + + + + diff --git a/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/thread3.0.2/libtcl9thread3.0.2.so b/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/thread3.0.2/libtcl9thread3.0.2.so new file mode 100644 index 0000000000000000000000000000000000000000..942beddfe2eec726e39d540dfe29e175ae5035f4 GIT binary patch literal 90483 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 Tvx4VO8G+Li7C=c~@T>;_VP*8s literal 0 HcmV?d00001 diff --git a/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/thread3.0.2/pkgIndex.tcl b/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/thread3.0.2/pkgIndex.tcl new file mode 100644 index 00000000..a6a7a228 --- /dev/null +++ b/src/vendorlib_tcl9/linux-x86_64/thread3.0.2/thread3.0.2/pkgIndex.tcl @@ -0,0 +1,55 @@ +# -*- tcl -*- +# Tcl package index file, version 1.1 +# + +# Tcl 8.7 interps are only supported on 32-bit platforms. +# Lower than that is never supported. Bye! +if {![package vsatisfies [package provide Tcl] 9.0] + && ((![package vsatisfies [package provide Tcl] 8.7]) + || ($::tcl_platform(pointerSize)!=4))} { + return +} + +# All Tcl 8.7+ interps can [load] thread 3.0.2 +# +# For interps that are not thread-enabled, we still call [package ifneeded]. +# This is contrary to the usual convention, but is a good idea because we +# cannot imagine any other version of thread that might succeed in a +# thread-disabled interp. There's nothing to gain by yielding to other +# competing callers of [package ifneeded Thread]. On the other hand, +# deferring the error has the advantage that a script calling +# [package require Thread] in a thread-disabled interp gets an error message +# about a thread-disabled interp, instead of the message +# "can't find package thread". + +package ifneeded [string tolower thread] 3.0.2 \ + [list load [file join $dir libtcl9thread3.0.2.so] [string totitle thread]] +package ifneeded [string totitle thread] 3.0.2 \ + [list package require -exact [string tolower thread] 3.0.2] + +# package ttrace uses some support machinery. + +# In Tcl 8.7+ interps; use [::apply] + +package ifneeded ttrace 3.0.2 [list ::apply {{dir} { + if {[info exists ::env(TCL_THREAD_LIBRARY)] && + [file readable $::env(TCL_THREAD_LIBRARY)/ttrace.tcl]} { + source $::env(TCL_THREAD_LIBRARY)/ttrace.tcl + } elseif {[file readable [file join $dir .. lib ttrace.tcl]]} { + source [file join $dir .. lib ttrace.tcl] + } elseif {[file readable [file join $dir ttrace.tcl]]} { + source [file join $dir ttrace.tcl] + } elseif {[file exists //zipfs:/lib/thread/ttrace.tcl] || + ![catch {zipfs mount [file join $dir libtcl9thread3.0.2.so] //zipfs:/lib/thread}]} { + source //zipfs:/lib/thread/ttrace.tcl + } + if {[namespace which ::ttrace::update] ne ""} { + ::ttrace::update + } +}} $dir] +package ifneeded Ttrace 3.0.2 \ + [list package require -exact ttrace 3.0.2] + + + +