|
|
set -- "$@" "a=[list shebangless punk MULTISHELL tclsh sh bash cmd pwsh powershell;proc Hide s {proc $s args {}}; Hide :;rename set s2;Hide set;s2 1 list]"; set -- : "$@"; $1 = @' |
|
|
: heredoc1 - hide from powershell (close sqote for unix shells) ' \ |
|
|
: << 'HEREDOC1B_HIDE_FROM_BASH_AND_SH' |
|
|
: .bat/.cmd launch section, leading colon hides from cmd, trailing slash hides next line from tcl \ |
|
|
: "[Hide @ECHO; Hide ); Hide (;Hide echo]#not necessary but can help avoid errs in testing" |
|
|
: Continuation char at end of this line and rem with curly-braces used to exlude Tcl from the whole cmd block \ |
|
|
@REM { |
|
|
@REM DO NOT MODIFY FIRST LINE OF THIS SCRIPT. shebang #! line is not required and will reduce functionality. |
|
|
@REM Even comment lines can be part of the functionality of this script - modify with care. |
|
|
@REM Change the value of nextshell in the next line if desired, and code within payload sections as appropriate. |
|
|
@SET "nextshell=pwsh" |
|
|
@REM nextshell set to pwsh,sh,bash or tclsh |
|
|
@REM @ECHO nextshell is %nextshell% |
|
|
@SET "validshells=pwsh,sh,bash,tclsh,tclkitsh,tclkit86bi" |
|
|
@CALL SET keyRemoved=%%validshells:%nextshell%=%% |
|
|
@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) |
|
|
@REM -- This section intended only to launch the next shell |
|
|
@REM -- Avoid customising this if possible. cmd/batch script is probably the least expressive language. |
|
|
@REM -- custom windows payloads should be in powershell,tclsh or sh/bash code sections |
|
|
@REM ## ### ### ### ### ### ### ### ### ### ### ### ### ### |
|
|
@SETLOCAL EnableExtensions EnableDelayedExpansion |
|
|
@SET "winpath=%~dp0" |
|
|
@SET "fname=%~nx0" |
|
|
@REM @ECHO fname %fname% |
|
|
@REM @ECHO winpath %winpath% |
|
|
@IF %nextshell%==pwsh ( |
|
|
CALL pwsh -nop -c set-executionpolicy -Scope CurrentUser RemoteSigned |
|
|
COPY "%~dp0%~n0.cmd" "%~dp0%~n0.ps1" >NUL |
|
|
REM test availability of preferred option of powershell7+ pwsh |
|
|
CALL pwsh -nop -nol -c write-host "statusmessage: pwsh-found" >NUL |
|
|
SET pwshtest_exitcode=!errorlevel! |
|
|
REM ECHO pwshtest_exitcode !pwshtest_exitcode! |
|
|
IF !pwshtest_exitcode!==0 CALL pwsh -nop -nol "%~dp0%~n0.ps1" %* & SET task_exitcode=!errorlevel! |
|
|
REM fallback to powershell if pwsh failed |
|
|
IF NOT !pwshtest_exitcode!==0 ( |
|
|
REM CALL powershell -nop -nol -c write-host powershell-found |
|
|
CALL powershell -nop -nol -file "%~dp0%~n0.ps1" %* |
|
|
SET task_exitcode=!errorlevel! |
|
|
) |
|
|
) ELSE ( |
|
|
IF %nextshell%==bash ( |
|
|
CALL :getWslPath %winpath% wslpath |
|
|
REM ECHO wslfullpath "!wslpath!%fname%" |
|
|
CALL %nextshell% "!wslpath!%fname%" %* & SET task_exitcode=!errorlevel! |
|
|
) ELSE ( |
|
|
REM probably tclsh or sh |
|
|
IF NOT "x%keyRemoved%"=="x%validshells%" ( |
|
|
REM sh 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 |
|
|
CALL %nextshell% "%~dp0%fname%" %* & SET task_exitcode=!errorlevel! |
|
|
) ELSE ( |
|
|
ECHO %fname% has invalid nextshell value %nextshell% valid options are %validshells% |
|
|
SET task_exitcode=66 |
|
|
GOTO :exit |
|
|
) |
|
|
) |
|
|
) |
|
|
@GOTO :endlib |
|
|
:getWslPath |
|
|
@SETLOCAL |
|
|
@SET "_path=%~p1" |
|
|
@SET "name=%~nx1" |
|
|
@SET "drive=%~d1" |
|
|
@SET "rtrn=%~2" |
|
|
@SET "result=/mnt/%drive:~0,1%%_path:\=/%%name%" |
|
|
@ENDLOCAL & ( |
|
|
@if "%~2" neq "" ( |
|
|
SET "%rtrn%=%result%" |
|
|
) ELSE ( |
|
|
ECHO %result% |
|
|
) |
|
|
) |
|
|
@GOTO :eof |
|
|
:endlib |
|
|
|
|
|
: \ |
|
|
@REM @SET taskexit_code=!errorlevel! & goto :exit |
|
|
@GOTO :exit |
|
|
# } |
|
|
# rem call %nextshell% "%~dp0%~n0.cmd" %* |
|
|
# -*- 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 s2 set; set k {-- "$@" "a}; if {[info exists ::env($k)]} {unset ::env($k)} ;# tidyup |
|
|
Hide :exit;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]/--]] |
|
|
} |
|
|
return [set ::punk::multishell::is_main($script_name)] |
|
|
} |
|
|
} |
|
|
} |
|
|
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin Tcl Payload |
|
|
#puts "script : [info script]" |
|
|
#puts "argcount : $::argc" |
|
|
#puts "argvalues: $::argv" |
|
|
#puts "argv0 : $::argv0" |
|
|
# -- --- --- --- --- --- --- --- --- --- --- --- |
|
|
|
|
|
|
|
|
#<tcl-payload> |
|
|
# -*- tcl -*- \ |
|
|
# 'build.tcl' name as required by kettle |
|
|
# Can be run directly - but also using `pmix Kettle ...` or `pmix KettleShell ...` |
|
|
|
|
|
|
|
|
|
|
|
#exec ./kettle -f "$0" "${1+$@}" |
|
|
kettle doc |
|
|
|
|
|
|
|
|
|
|
|
#</tcl-payload> |
|
|
|
|
|
|
|
|
|
|
|
# -- --- --- --- --- --- --- --- --- --- --- --- |
|
|
# -- 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 |
|
|
# sh/bash \ |
|
|
shift && set -- "${@:1:$#-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 @call line above ... to something like @call sh ... @call bash .. or @call env sh ... etc as appropriate |
|
|
# -- if sh/bash scripting needs to run on windows too. |
|
|
# -- |
|
|
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### |
|
|
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin sh Payload |
|
|
#printf "start of bash or sh code" |
|
|
|
|
|
#<shell-payload-pre-tcl> |
|
|
#</shell-payload-pre-tcl> |
|
|
|
|
|
# -- --- --- --- --- --- --- --- |
|
|
#<shell-launch-tcl> |
|
|
exitcode=0 ;#default assumption |
|
|
#-- 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 "tcl exitcode: ${exitcode}" |
|
|
#-- override exitcode example |
|
|
#exit 66 |
|
|
#</shell-launch-tcl> |
|
|
# -- --- --- --- --- --- --- --- |
|
|
|
|
|
#<shell-payload-post-tcl> |
|
|
#</shell-payload-post-tcl> |
|
|
|
|
|
|
|
|
#printf "sh/bash done \n" |
|
|
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end sh Payload |
|
|
#------------------------------------------------------ |
|
|
fi |
|
|
exit ${exitcode} |
|
|
# end hide sh/bash 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 |
|
|
# -- |
|
|
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### |
|
|
function GetScriptName { $myInvocation.ScriptName } |
|
|
$scriptname = getScriptName |
|
|
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin powershell Payload |
|
|
#"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 |
|
|
# -- --- --- --- |
|
|
|
|
|
#<powershell-payload-pre-tcl> |
|
|
#</powershell-payload-pre-tcl> |
|
|
|
|
|
|
|
|
# -- --- --- --- --- --- --- --- |
|
|
#<powershell-launch-tcl> |
|
|
tclsh $scriptname $args |
|
|
#</powershell-launch-tcl> |
|
|
# -- --- --- --- --- --- --- --- |
|
|
|
|
|
|
|
|
#<powershell-payload-post-tcl> |
|
|
#</powershell-payload-post-tcl> |
|
|
|
|
|
# unbal } |
|
|
|
|
|
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end powershell Payload |
|
|
#"powershell reporting exitcode: {0}" -f $LASTEXITCODE | write-host |
|
|
Exit $LASTEXITCODE |
|
|
# heredoc2 for powershell to ignore block below |
|
|
$1 = @' |
|
|
' |
|
|
: end hide powershell-block from Tcl \ |
|
|
# This comment with closing brace should stay in place whether 'if' commented or not } |
|
|
: cmd exit label - return exitcode |
|
|
:exit |
|
|
: \ |
|
|
@REM @ECHO exitcode: !task_exitcode! |
|
|
: \ |
|
|
@EXIT /B !task_exitcode! |
|
|
# cmd has exited |
|
|
: end heredoc2 \ |
|
|
'@ |
|
|
<# |
|
|
# id:tailblock0 |
|
|
# -- powershell multiline comment |
|
|
#> |
|
|
<# |
|
|
# id:tailblock1 |
|
|
# <ctrl-z> |
|
|
|
|
|
# </ctrl-z> |
|
|
# -- unreachable by tcl directly if ctrl-z character is in the <ctrl-z> 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) |
|
|
#> |
|
|
|
|
|
|
|
|
|