<#
Standard profile for psec, the PowerShell Enhanced Controller.
Note that the functions seem to be replaced only when Powershell is restarted
This imbeds a customized powershell command structure so the the interface cab be augmented
for the users.
History:
EC3309 - Allow flow and flows to have multiple directories
Original EC8704
#>
<##.mod-doc
start-psec.ps1
==============
_PSEC version 0.0.9_
#### PSEC bootstrap ####
This script file contains the routines that Powershell calls upon initiation as specified by the -file parameter of the
Powershell shortcut.
Since it is the first level, functions are treated as commands by Powershell. They are
documented as _cmds_ in this documentation.
Functions in this script file that are in reality internal functions have a special flag that
triggers the dgen.ps1 routines to treat them as such.
#### PSEC Restart mechanism ####
The Powershell console task has the ability to return an integer ExitCode (with defualt of 0).
Normally this is inspected by Windows and in most us cases for PowerShell would be ignored.
In the case where a secondary shell is started using the @@new, the return code from the secondary
console window is inspected. If it is 777 the @@NEW is reissued.
This gives the secondary shell the ability to restart itself. This is used primarily during development of the system modules and classes.
It gives an expwdient way to reload the runtime environment and thus work with the refreshed code after changes have been made.
The @@xx command is an easy way to trigger the 777 exit code. Where the new code will not parse due to
compile errors typing exit 777 will accomplish the same thing.
The @@GUI routines also make use of this facility by programtically issuing the exit code 777 where necessary. Refer to @@GuiRestart.
#>
using module .\lib\psec-v4-shr.psm1
using module .\lib\dlog.psm1
[reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
[reflection.assembly]::LoadWithPartialName("System.Drawing.Icon")
Add-Type -AssemblyName PresentationCore,PresentationFramework
"starting PSEC the PowerShell Enhanced Controller using $PSCommandPath $($args.count) $($args[0..$args.count])"
set-strictmode -version 2.0
#-- global class definitions
<#.md
.desc
Global Communication Vector Table
.details
Contains a globally accessable table of important values and class pointers
Can be inspected from the command line using the see cmd. eg see $CVT
.history
In the original version of PSEC it allowed complex JOB structures to be built when entering a
particular folder. This turned out to be cumbersome and of little value.
This version anticipates a simpler CVT and the ability to add and subtract run scripts based on
entering a folder with a signature script. It is not yet implemented but the CVT remains in place in
preparation for this.
#>
class CVT {
#---- properties ----
[string]$psecVer;
[string]$psecStart;
[string]$psecBase;
[string]$iconTheme = 'cyn'; # color theme. See cc cmd
[boolean]$bSecCons; # true if Running as secondary console
[hashtable]$locns = @{};
[hashtable]$psec = @{};
[hashtable]$run = @{};
[hashtable]$std = @{};
[hashtable]$favs = @{};
[GlobUtil]$gutil = $null;
#---- constructors ----
static [CVT] create([string]$psecVer,[string]$psecStart) {
$CVT = [CVT]::new();
$CVT.psecVer = $psecVer;
$CVT.psecStart = $psecStart;
$CVT.psecBase = $psecStart | Split-Path -Parent;
# format stabndard date hashtable
$curDate = [string](get-Date).addDays(0);
$curYear = "$curDate".substring(6,4);
$CVT.std.curDate = "$curDate".substring(0,10);
setMonth "$curDate".substring(0,2);
$CVT.std.curDay = "$curDate".substring(3,2);
setYear $curYear
hilite "CVT created for $($CVT.psecVer) in $($CVT.psecStart)";
return $CVT;
}
}
<# This class installs a number of useful functions. It also means
we can revert to the more tranitional syntax for function calls.
#>
<#.md
.desc
Global methods stored in the g global variable
.details
Allows for methods to be called in the more regular syntax of a function and not a
Powershell command
#>
class GlobUtil {
#---- properties ----
#---- constructors ----
#---- methods ----
[void]log([string]$msg) {
hilite $msg
}
}
# Note. For dgen parsing classes must be defined before this point.
# --- dgen end classes --
## ================================= functions =================================
<# -------------------------------------------------
These functions need to be defined here because they
are used during startup
----------------------------------------------------#>
<#.md
.desc
This sets a standard 4 char date seq that is sortable.
It wraps every decade.
.hfunc
.notes
#>
function ECID {
$date = Get-Date
$ecid = makeECID($date)
[void]("ECID set to $($CVT.psec.ECID)")
return $ECID
}
<#.md
.desc
This makes a standard 4 char date seq that is sortable.
It wraps every decade.
.hfunc
.notes
Returns a 4 character string as follows"
* Year: 0 thru 9
* Month: 1,2,3,4,5,6,7,8,9,a,b,c
* Day: 01 thru 31
#>
function makeECID($date) {
# param($date)
[string]$dateStr = $date.ToString("yyyyMMdd")
$months = "0123456789ABC"
[int]$mon = $datestr.substring(4,2)
$ecid = $datestr.substring(3,1)+$months.substring($mon,1)+$datestr.substring(6,2)
$CVT.psec.ECID = $ecid
return $ecid
}
<#.md
.desc
return $true if property exists
.hfunc
.notes
used in strict mode to validate property exists before using
This can be used on hashtables, custom objects or class files.
EC:9C10 Fails on hashTables if value is null. use $ht.containsKey($key)
#>
function exists($obj,$prop) {
try {
if ($null -ne $obj[$prop]) {return $true}
return $false
} catch {
return $false
}
return $false
}
<#.md
.desc
Converse of @@exists
.hfunc
#>
function notExists($obj,$prop) {
if (exists $obj $prop) {return $false;}
return $true;
}
<#.md
.desc
return $true if property exists and has a value other than "" or $null
.hfunc
.notes
only works for hashtables
A value of 0 returns $true unlike Javascript
#>
function valExists([hashtable]$obj,$prop) {
if (!(exists $obj $prop)) {return $false;}
if ("$($obj[$prop])" -eq "") {return $false;}
return $true;
}
<#.md
.desc
return value if exists else $null
.hfunc
#>
function optval($obj,$prop) {
#param($obj,$prop)
if (exists $obj $prop) {return $obj[$prop]}
return $null
}
<#.md
.desc
return count of list or 0 if $null
.hfunc
#>
function count($list) {
if ($list -eq $null) {return 0;}
return $list.count;
}
<#.md
.desc
return $true if 2 objects are the same
.hfunc
.notes
Uses .NET [Object]::ReferenceEquals test to see if two object references are the same object
#>
function isSameObj($o1,$o2) {
#param($o1,$o2)
return [System.Object]::ReferenceEquals($o1,$o2);
}
<#.md
.desc
Terniary replacement. Return $val if $cond true, else $notVal
.hfunc
.notes
To force a Condition (boolean) result sometimes the \$cond operators need to be resequenced. Thus use (\$null -eq \$arr) and not
($arr -eq \$null) (The \$arr is assumed to be boolean).
Alternately it could be expressed in pure Powershell syntax. Thus:
\$x = if (condition) {\$true-val} else {\$false-value}
.examples
\$arr = (tern (\$null -eq \$arr) \$vals \$arr);
#>
function tern([boolean]$cond,$val,$notVal) {
if ($cond) {return $val;}
return $notVal;
}
<#.md
.desc
Sets the $CVT std.year, std.prevYear, std.nextYear
.hfunc
#>
function setYear([int]$curYear) {
$nxtYear = ($curYear-0)+1;
$prvYear = ($curYear-0)-1;
$CVT.std.year = "$curYear";
$CVT.std.prevYear = "$prvYear";
$CVT.std.nextYear = "$nxtYear";
[void]("Set year $CurYear prev=$prvYear next=$nxtYear")
}
<#.md
.desc
Sets the $CVT std.curMonth, std.prevYear, std.nextYear
.hfunc
#>
function setMonth([int]$curMon) {
$CVT.std.curMonth = "$(100+$curMon)".substring(1);
$CVT.std.picMonth = "JanFebMarAprMayJunJulAugSepOctNovDec".substring(($CVT.std.curMonth - 1) * 3,3);
$CVT.std.picPrevMonth = "DecJanFebMarAprMayJunJulAugSepOctNovDec".substring(($CVT.std.curMonth - 1) * 3,3);
}
<#.md
.desc
write $args[0] directly to host logger (with debug location when available)
.hfunc
.notes
When called from a script it prefixes the message with the script location as a debugging aid.
There is only 1 paramater so the more traditional hlog(\" . . . \") syntax can be used.
#>
function hlog {
$locn = fmtLocn
$Host.UI.WriteLine("$($locn) $($args[0])")
}
<#.md
.desc
write $args[0] directly to the debug file
.hfunc
.notes
It assumes the debug file pointer has been set up.
There is only 1 paramater so the more traditional hlog(\" . . . \") syntax can be used.
#>
function dlog {
$locn = fmtLocn
$dlog = $GLOBAL:DLOG
$dlog.info("$($locn) $($args[0])")
}
function flush {
$dlog = $GLOBAL:DLOG
$dlog.info("----- flushed -----")
$dlog.flush();
}
function testd([String]$msg) {
dlog($msg)
}
<#.md
.desc
writeErrorLine $args[0] directly to host logger
.hfunc
.notes
When called from a script it prefixes the message with the script location as a debugging aid.
#>
function hlogErr {
$locn = fmtLocn
$Host.UI.WriteErrorLine("$($locn) $($args[0])")
}
<#.md
.desc
writeWarningLine $args[0] directly to host logger
.hfunc
.notes
When called from a script it prefixes the message with the script location as a debugging aid.
#>
function hlogWarn {
$locn = fmtLocn
$Host.UI.WriteWarningLine("$($locn) $($args[0])")
}
<#.md
.desc
write $args[0] directly to host logger with blue on white background
.hfunc
.notes
When called from a script it prefixes the message with the script location as a debugging aid.
#>
function hNote {
$locn = fmtLocn
$Host.UI.WriteLine('Blue','White',"$($locn) $($args[0])")
}
<#.md
.desc
write $args[0] directly to host logger with cyan foreground color
.hfunc
.notes
When called from a script it prefixes the message with the script location as a debugging aid.
#>
function hFlag {
$locn = fmtLocn
$BG = $host.ui.rawui.BackgroundColor;
$Host.UI.WriteLine('Cyan',$BG,"$($locn) $($args[0])")
}
function fmtLocn() {
$csf = get-PSCallStack
$scr = $csf.scriptname[2]
if ($scr -ne $null) {
$lix = $scr.lastIndexOf("\");
if ($lix -gt 0) {$scr = $scr.substring($lix + 1)}
$scr = $scr -replace '\.(psm1|ps1)',''
$pos = $csf.scriptlinenumber[2]
} else {
return ""
}
return "[$($scr)@$($pos)] "
}
<#.md
.desc
Create a runlog record for timesheet creation
.hfunc
.notes
Used to create a runlog record and append to the file defined by ENV:RUNLOG
This is used to create logging records so the timestamps can be used to determine
approximate activity on a system and therefore a project.
#>
function runlog($desc) {
[string]$log = $ENV:RUNLOG
if ($log -eq "") {return}
$sys = $ENV:COMPUTERNAME
$date = Get-Date
[string]$dateStr = $date.ToString("yyyyMMdd hh:mm:ss")
$str = "$sys $datestr $PWD $desc";
hilite -yellow "runlog:$str"
add-content $log "$($str)"
}
$GLOBAL:CVT = [CVT]::create('PSEC.v0.9',$PSCommandPath);
$reload = "reloaded"
if (Test-Path alias:cd) {
# This has the side-effect of being a f/t switch so we do
}
<#.md
.desc
parse the active scripts for #@ statements and create table for @@hh
.hfunc
.notes
#>
function hhParse {
if (test-path config.ps1) { ## Obsolete
$file = $(Get-Item config.ps1)
if ($file.LastWriteTime -gt $CVT.run.configLastWriteTime) {
"config has changed $curDate $($CVT.run.configLastWriteTime)"
$CVT.run.configLastWriteTime = $file.LastWriteTime
installConfig($null);
}
}
[void](hilite -yellow "hhParse.v4 $PSCommandPath")
$t = @{}
$text = get-content $PSCommandPath -encoding ascii
[boolean]$bInCmd = $false;
[boolean]$bMarkDown = $false;
[string]$targ = $null;
[string]$curCmd = $null;
foreach ($line in $text.split("`n")) {
if ($line -match "^#[@]") {
$bInCmd = $true;
#hilite -magenta "line: $line"
# 1 23 4
if ($($line.substring(2)) -match ' *([^ ]+) (([^;]+);)?;? *(.*)') {
$curCmd = $matches[1];
$t[$curCmd] = @{title=$matches[4];syntax="$($matches[1]) $($matches[3])".trim();type='cmd'}
}
}
if ($bInCmd) {
if ($line -match "^<#.md") {
$bMarkDown = $true;
$targ = $null;
}
}
if ($bMarkDown) {
if ($line -match "^[.]([a-z]+)$") {
$targ = $matches[1]
($t[$curCmd])[$targ] = ""
} else {
if ($line -match "^ *#>") {
$bMarkDown = $false;
$targ = $null;
} elseif ($targ -ne $null) {
($t[$curCmd])[$targ] += "`r`n$line"
}
}
} else {
if ($line -match '^ *function +([a-zA-Z][a-zA-Z0-9_]*) *[{]') {
$func = $matches[1]
if ($t[$func] -eq $null) {
$t[$func] = @{title='not-defined';type='priv';syntax=""}
}
$bInCmd = $false;
}
}
}
foreach ($line in $text.split("`n")) {
##if ($line -match "^##[@]") {"$($line.substring(3))"}
##"$($line.toString().substring(3))"
}
if (test-path config.ps1) { ## Obsolete
$config = $(Get-Item config.ps1)
$ht = getInstalled($null);
foreach($key in $ht.Keys) {
$func = $ht[$key]
$dyn = "run"
if (exists $func invoke) {
$func = $run = (& $func)
$dyn = "dyn-run"
$title = "?";
} elseif ("$(easyType($func))" -eq "scriptblock") {
$run = (& $func)
if (exists $run title) {$title = $run.title}
}
if ((exists $func alias) -and ($func.alias -eq $key)) {continue}
if (exists $func title) {$title = $func.title}
$t[$key] = @{title=$title;type=$dyn;alias=''}
if (exists $func alias) {
$t[$key].alias = $($func.alias)
}
}
}
return $t;
}
#@ help ; Display PSEC help in default browser
<#.md
.desc
The help command is used to display the PSEC generated help
in the default browser
#>
function help {
$browser = (& getDefaultBrowserPath)
[string]$what = "$($args[0])";
if ($what -ne "") {$what="?$($what)"}
[void](& $browser "$($env:PSEC_V4_DOCS_DIR)\index.html$($what)")
hilite "start $browser $($env:PSEC_V4_DOCS_DIR)\index.html$($what)"
}
function getDefaultBrowserPath {
#Get the default Browser path
New-PSDrive -Name HKCR -PSProvider registry -Root Hkey_Classes_Root | Out-Null
[string]$browserPath = (Get-ItemProperty ‘HKCR:\http\shell\open\command’)
#v $browserpath
if ($browserpath -match '\(default\)="([^"]+)"') {
return $matches[1]
} else {
throw "lost default browser $browserPath"
}
}
#@ hh [specific]; Generate this help extract
<#.md
.desc
The @@hh is used to display the command syntax for all of the
additional commands implemented in PSEC.
The run commands available in the curApp are also listed.
The hh specfic format displays detailed information on the specific
command. Thus
hh gui
would display detailed help on the @@gui.
#>
function hh {
[hashTable]$t = (& hhParse);
<#
if ($CVT.curApp -ne $null) {
forEach($jobKey in $CVT.curApp.jobs.keys) {
$job = $CVT.curApp.jobs[$jobKey]
if ("$($job.name)" -eq $jobKey) {
#hlog("job $jobKey $($job.getType()) $($job.name)");
$syntax = "script"
$t["r:$($job.name)"] = @{title=$job.title;type='run';alias="$($job.alias)";syntax=$syntax}
}
}
}
#>
if ($args.count -eq 1) {
if (notExists $t $args[0]) {
hilite -red "command $($args[0]) does not exist"
}
[hashtable]$hh = $t[$args[0]]
hilite -cyan "Details for command $($args[0])"
hilite "Purpose: $($hh.title)"
hilite " Syntax: $($hh.syntax)"
if (exists $hh desc) {
hilite "`r`nDescription:`r`n============`r`n$($hh.desc)"
}
if (exists $hh notes) {
hilite "`r`nNotes:`r`n======`r`n$($hh.notes)"
}
if (exists $hh example) {
hilite "`r`nExample:`r`n========`r`n$($hh.example)"
}
return;
}
"====== Help for 'func' commands in $PSCommandPath and installed run commands ======"
#" Type run or run-dyn for '$($CVT.std.config)' installed from $config"
hilite -cyan "----Name---- --Type-- Alias -----Syntax------ --- Description ---... $($args[0])"
$selType = $args[0]
$t.GetEnumerator() | sort -Property name | forEach {
$alias = "";
$title = "";
$syntax = "";
if (exists $_.value alias) {$alias = $_.value.alias;}
if (exists $_.value title) {$title = $_.value.title;}
if (exists $_.value syntax) {$syntax = $_.value.syntax;}
if (($_.value.type -eq 'cmd') -or ($_.value.type -eq 'run')) {
if (($selType -eq $null) -or ($selType -eq $_.value.type)) {
"$($($_.name).PadRight(13)) $($($_.value.type).padRight(8)) $($alias.padRight(6)) $($syntax.padRight(17)) $($title)"
}
}
}
#$config | get-member | out-gridview
}
#@ year 20yy[mm]; Change accounting year/month to 20yy[mm]
<#.md
.desc
The year is stored in the CVT and is calculated during PSEC initiation.
This allows the computed value to be changed to 20yy and nextYear and PrevYear
to also be recalulated.
If the optional month is provided, the current month values are also changed.
.notes
Many task use this value to set their parameters
#>
function year {
$year = $args[0]
if ($year -match '^20[0-9][0-9]([0-9]{2})?$') {
setYear $year
if ($matches[1] -ne $null) {
setMonth "$($matches[1])"
}
} else {
" INVALID year $year. must start with 20"
}
}
#@ cd dir; Change current directory, (reload config.ps1 Obsolete)
<#.md
.desc
Change the current PSEC directory. If the new directory contains
the file *config.ps1* it is executed and assumed to set up
functions that can be accessed with the run command.
.notes
This intercepts and changes the standard CD behavoir.
#>
function cd {
$("changing location to " + $args)
$CVT.run.cwd = $args[0]
$file = $args[0] | Split-Path -Leaf
$CVT.run.cwdStart = "start-$(file).ps1"
[void]("starter is $($CVT.run.cwdStart)")
set-location -path $($args[0].toString())
If (Test-Path ("config.ps1")) { ## Obsolete
$file = $(Get-Item config.ps1)
$CVT.run.configLastWriteTime = $file.LastWriteTime
"timestamp $($CVT.run.configLastWriteTime) $file"
installConfig($null);
}
}
#@ cdd dir; Change current drive and directory, (reload config.ps1 Obsolete)
<#.md
.desc
Change the current PSEC drive and directory. If the new directory contains
the file *config.ps1* it is executed and assuned to set up
functions that can be accessed with the run command.
.notes
This intercepts and changes the standard CDD behavoir.
#>
function cdd {
$("changing drive and location to " + $args)
set-location -path "$($args[0])"
$CVT.run.cwd = $args[0]
If (Test-Path ("config.ps1")) { ## Obsolete
$file = $(Get-Item config.ps1)
$CVT.run.configLastWriteTime = $file.LastWriteTime
"timestamp $($CVT.run.configLastWriteTime) $file"
installConfig($null);
}
}
#@ dgen [copylocn]; generate PSEC docs (developer convenience cmd)
<#.md
.desc
This runs the generation of the PSEC docs. It is provided as a convenience to the
developer as is not normally run.
.notes
If args[0] points to a folder the resulting documention is copied to that location. Useful for
browsing from another machine.
The current directory is changed by this command.
#>
function dgen {
cdd ($CVT.psecBase)
./scr/dgen.ps1 $args[0]
}
#@ gui [c] [app] args; run gui app with args. c option adds console for debug
<#.md
.desc
Starts the GUI processing engine. If *app* is usually specified, and is a GUI application in the
gui-native-forms.psm1 module.
The start-gue.ps1 (a lightweight psec-start.psi replacement) is used to drive the window
and by default runs in a hidden window unless the c option above is included.
If no application is apecified, 'test1' is invoked to test the gui.
.notes
This implements the @@GuiRestart facility.
Using the X in the top right corner to close the application or typing ./
will prevent restarting the application.
#>
function gui {
$bConsole = $false;
$what = "test1";
$bias = 0;
if ($args.length -gt 0) {
if ($args[0] -eq "c") {
$bConsole = $true;
$bias += 1;
}
}
if ($args.length -gt $bias) {
$what = $args[$bias];
}
if ($bConsole) {
[void](& invokeGui c -native $what)
} else {
[void](& invokeGui -native $what)
}
}
#@ flow [c] app [args]; run gui app with args. c option adds console for debug
<#.md
.desc
Starts the GUI processing engine. If *app* is required and is the Workflow application in the
$env:PSEC_V4_FLOWS folder.
The start-gui-v4.ps1 (a lightweight start-psec-v4.ps1 replacement) is used to drive the window
and by default runs in a hidden window unless the c option above is included.
.notes
This implements the @@link.restart facility (triggered by typing // when the window has focus).
Using the X in the top right corner to close the application or typing ./
will prevent restarting the application.
#>
function flow {
$bConsole = $false;
$what = "*prev*";
$bias = 0;
if ($args.length -gt 0) {
if ($args[0] -eq "c") {
$bConsole = $true;
$bias += 1;
}
}
if ($args.length -gt $bias) {
$what = $args[$bias];
}
if ($bConsole) {
[void](& invokeGui c -flow $what)
} else {
[void](& invokeGui -flow $what)
}
}
function hlogReal {
$Host.UI.WriteLine("$($args[0])")
}
<##
This creates a new Powershell process that runs eith with the console or not. The start file is the psec-gui variant
that starts the GUI process. This is either a native GUI app or a workflow.
#>
function invokeGui {
hlogReal("invokeGui");
$gui = "$PSCommandPath" -replace "start-psec","start-gui"
$dlog = $GLOBAL:DLOG;
$dlogGui = $($dlog.filePath) -replace "-psec-","-gui-";
$bNoConsole = $true;
$ix = 0;
$what = "?";
$name = "?";
while($ix -lt $args.length) {
$arg = $args[$ix]
if ($arg -eq "c") {
$bNoConsole = $false;
} elseif ($arg -eq "-native") {
$what = $arg;
} elseif ($arg -eq "-flow") {
$what = $arg;
} else {
$name = $arg;
}
$ix += 1
}
$argList = @("-noprofile","-noexit");
$showConsole = "-show";
if ($bNoConsole) {
$argList += "-WindowStyle Hidden"
$showConsole = "-hide";
$host.ui.writeLine("red","white","Console hidden. Expect several second configuration delay")
}
$argList += "-file $gui"
$argList += "-dlog $dlogGui"
$argList += "$showConsole"
$argList += "$what $name"
while($true) {
hlogReal("args $argList")
$process = (start-process powershell.exe -argumentlist $arglist -PassThru -Wait)
hlogReal("exitCode $($process.exitCode )")
if ($process.exitCode -ne 901) {break;} # used by start-gui-v4.ps1 to indicate restart
}
}
#@ env [arg]; list environment variables
<#.md
.desc
Used to list environment variables from existing context. issues dir env:arg
.notes
The first arg can be used to shorten the list. eg env n* or env npm
#>
function env {
dir env:$($args[0])
}
#@ cc theme or bg fg; change console color and icon
<#.md
.desc
Used to change the console color. A single value themed color. Otherwise bg fg. Themes also change icon
.notes
Theme BG FG
cyn darkBlue white (Default theme)
pnk darkMagenta white
pur darkCyan white
red darkRed white
org yellow black
grn darkGreen white
yel darkYellow white
gry darkGray white
blk black white
#>
function cc {
$h = (Get-Host).UI.RawUI;
if ($args.count -eq 1) {
$h.foregroundColor = 'white' ;
if ($args[0] -eq "cyn") {$h.backgroundColor = 'darkCyan'; $h.foregroundColor = 'white'; zapIcon("cyn");}
if ($args[0] -eq "pnk") {$h.backgroundColor = 'darkMagenta'; $h.foregroundColor = 'white'; zapIcon("pnk");}
if ($args[0] -eq "pur") {$h.backgroundColor = 'darkBlue'; $h.foregroundColor = 'white'; zapIcon("pur");}
if ($args[0] -eq "red") {$h.backgroundColor = 'darkRed'; $h.foregroundColor = 'white'; zapIcon("red");}
if ($args[0] -eq "yel") {$h.backgroundColor = 'yellow'; $h.foregroundColor = 'black'; zapIcon("org");}
if ($args[0] -eq "grn") {$h.backgroundColor = 'darkGreen'; $h.foregroundColor = 'white'; zapIcon("grn");}
if ($args[0] -eq "org") {$h.backgroundColor = 'darkYellow'; $h.foregroundColor = 'white'; zapIcon("yel");}
if ($args[0] -eq "gry") {$h.backgroundColor = 'darkGray'; $h.foregroundColor = 'white'; zapIcon("gry");}
if ($args[0] -eq "blk") {$h.backgroundColor = 'black'; $h.foregroundColor = 'white'; zapIcon("blk");}
clear-host #EC0204
}
if ($args.count -eq 2) {
$h.backgroundColor = $args[0];
$h.foregroundColor = $args[1];
clear-host #EC0204
}
}
function zapIcon($iconSel) {
$cons = "gtw" # Use white > symbol in icon
if ($CVT.bSecCons) { # Use red or green > symbol in icon
$cons = "gtR"
if ($iconSel -eq "red") {$cons = "gtG"};
}
hlog("ZapIcon $iconSel $($CVT.bSecCons)")
$iconName = "psec-$($iconSel)-$($cons).ico"
. "$($CVT.psecBase)/scr/change-icon.ps1" $iconName
$CVT.iconTheme = $iconSel;
}
#@ ct title; set console title to join args
<#.md
.desc
Used to change the console title. $args[0] thru $args[n] are joined to form title
#>
function ct {
$h = (Get-Host).UI.RawUI;
if ($args.count -gt 0) {
$str = "";
forEach($s in $args) {
$str += " $s"
}
$h.WindowTitle = $str.trim();
}
}
#@ cls; clear host console and retail color
<#.md
.desc
Used to clear host console. Windows 10 cls did not work
#>
function cls {
clear-host
}
#@ rr ;repeat run (used for Jetty)
<#.md
.desc
Certain commands store a copy of their initiation in the appJob class. This
command re-executes that command.
.notes
This is primarily used for the Jetty server (Google app engine) which has
trouble with the r builtin command to re-execute the previous command.
#>
function rr {
$CVT = $GLOBAL:CVT
if ($CVT.curApp.runRepeat) {
& run $CVT.curApp.runRepeat
} else {
hilite -red "nothing to repeat"
}
}
#@ flows ;list flows in flows directory
<#.md
.desc
Lhis lists the flows in the configured flows directory.
.notes
The flows directory is located from the PSEC_V4_FLOWS env variable.
#>
function flows {
if ("$($env:PSEC_V4_FLOWS)" -eq "") {
hLogErr "env variable psec_flows needs to be configured"
} else {
forEach ($dirPath in ("$($env:PSEC_V4_FLOWS)".Split(';'))) {
hilite -cyan "flows in dir $dirPath"
forEach($flow in (get-childitem $dirPath | sort-object -Property Name)) { # sort to get a stable logical, sequence
$flowCfg = "$($flow.fullName)/$($flow.name)-cfg.json"
if ((& isDir "$($flow.fullName)") -and (& test-path -path "$flowCfg")) {
$cfg = (get-item -path $flowCfg)
$TS = ($cfg.lastWriteTime)
$dateStr = (Get-Date -Date $TS -format "yyyy-MMM-dd HH:mm:ss")
"$(($flow.name).padRight(16)) $dateStr"
}
}
}
}
}
#@ run XXXX args; run XXXX with args defined in config.ps1
<#.md
.desc
run the XXXX application defined in the config.ps1 loaded by the CD or CDD command.
.notes
The hh command will show those that are currently active.
#>
function run {
hilite -cyan "run $($args[0]) len=$($args.length) called"
<#$curDate = (Get-Date).AddDays(0);
$file = $(Get-Item $CVT.curAppconfig.ps1)
if ($file.LastWriteTime -gt $CVT.run.configLastWriteTime) {
"config has changed $curDate $($CVT.run.configLastWriteTime)"
$CVT.run.configLastWriteTime = $file.LastWriteTime
installConfig($null);
} else {
"config the same $curDate file $($CVT.run.configLastWriteTime)"
}#>
if ($($args[0]) -eq "?") {
<#$("run query " + $args)
forEach($key in $($($CVT.chsol.jobs).keys)) {
"$key $($($CVT.chsol[$key]).title)"
}#>
hilite -red "$args[0] help not implemented yet"
} else {
$extra = @();
$i = -1;
forEach($arg in $args) {
$i += 1;
if ($i -gt 0) {
$extra += $arg
}
}
$CVT = $GLOBAL:CVT
if ($CVT.curApp -eq $null) {
hilite -red "no curApp installed"
return;
}
if (notExists $CVT.curApp.jobs $args[0]) {
hilite -red "no such job $($args[0]) in $($CVT.curApp.appName)"
return;
}
$CVT.curApp = $CVT.curApp.refresh();
[AppJob]$appJob = $CVT.curApp.jobs[$args[0]]
"calling $($appJob.title)"
& $appJob.script $appJob $extra #EC0125 - pass $extra
}
<#$pgm = $CVT[$CVT.std.config].jobs[$args[0]]
if ($pgm -eq $null) {
"pgm $($args[0]) not defined in config.ps1"
return
} elseif (!(exists $pgm invoke)) { #dynamic function
#& $pgm | out-gridview
$run = (& $pgm)
$run.extra = $extra
if (exists $run stopBefore) {
& $run.stopBefore $run
}
if (exists $run parseExtra) {
& $run.parseExtra $run
}
$loop = @($run.parms);
if ($($run.parms).getType() -eq 'System.Object[]') {$loop = $run.parms;}
if (exists $run main) {$main = $run.main} else { $main = $null}
hilite -green "exec dyn function $main $($run.title) loops=$($loop.length)"
forEach($runParms in $loop) { #Iterate thru parm set if $run.parms is an array
$parms = @();
if ($runParms -ne $null) {
forEach($key in $runParms.Keys) {
if ($key.substring($key.length-1,1) -eq "=") { # property
$parms += $("-def");
$parms += $($key+$($runParms[$key]))
} else {
$parms += $("-"+$key);
if ($runParms[$key] -ne $null) {$parms += $runParms[$key]}; # else boolean
}
}
}
if (exists $run main) {
. java -cp $($run.cp) $($run.main) $($parms+$($run.extra))
hilite -cyan "LastExitCode=$LastExitCode"
} elseif (exists $run node) {
##[Environment]::SetEnvironmentVariable("NODE_PATH","C:\c-pgms\NPM\node_modules","User");
. C:\c-pgms\node-js\node.exe $($($run.path)+'\'+$($run.node)) $($parms+$($run.extra))
} elseif (exists $run script) {
try {
if ($args.count -eq 1) {
. $run.script
} else {
. $run.script $args[1..($args.Count-1)]
}
} catch {
$e = $_
hLogErr("$($e.exception.message)")
hLogErr("$($e.ScriptStackTrace)")
}
} else {
hilite -red "main or node or script not defined for $($args[0])"
return
}
}
if (exists $run startAfter) {
& $run.startAfter $run
}
} else {
#$("run " + $pgm.title)
$parms = $pgm.parms
". java -cp $pgm.cp $pgm.class $($parms+$extra).split($sp) "
}#>
#}
}
#@ x ; close window
<#.md
.desc
closes the PSEC window with no restart
#>
function x {exit 0} # exit $lastExitCode set to 0
#@ xx ; close window, set restart flag
<#.md
.desc
closes the PSEC window with an exit code of 777 used to signal a restart
if the current window was started with the new command.
.notes
The command passed in with the new command will be re-executed.
To change the command, use x and then new again with the revised command.
Typing exit 777 will accomplish the same thing.
#>
function xx {exit 777} # # exit $set to 777 to indicate restart if running under new command
#@ v obj; display object in grid
<#.md
.desc
displays the specified object in a grid. Useful to inspect the '$CVT.
.notes
#>
function v($a1) {
$a1 | out-gridview
}
#@ vo obj; display object members in grid
<#.md
.desc
displays the members of the specified object in a grid. Useful to inspect the '$CVT.
.notes
#>
function vo($a1) {
$a1 | get-member | out-gridview
}
#@ objdef obj-inst [file]; display object definition to console (or file)
<#.md
.desc
Displays obj-inst on the console where obj-inst is a new-object constructor
.notes
The file parameter causes the text to be written to the specified file
Due to the vagueness of Powershell parameter parsing the new-object constructor
needs to be wrapped in parenthesis when the file parameter is included as
the second example shows.
.example
DirInfo: `objdef new-object system.io.directoryinfo('x')`
FileInfo: `objdef (new-object system.io.fileinfo('x')) c:\temp\dirinfo.txt`
#>
function objdef([object]$obj,[string]$file) {
if ($file -ne '') {
$obj | get-member | out-string | out-file -encoding ASCII $file
} else {
$obj | get-member | out-string
}
}
#@ qbak [xxxx]; [XXXX] backup of XXXX or default
<#.md
.desc
executes the QBAK.PS1 script to back up the configured folders.
.notes
* Requires winzip to be installed.
* Version 2 is the enhanced version. See qbak-v2.scr script help
#>
function qbak() {
import-module "$($CVT.psecBase)\gui\psm\gui-classes" -force
import-module "$($CVT.psecBase)\gui\psm\gui-utils" -force
. "$($CVT.psecBase)\scr\qbak-v2.ps1" @args
}
#@ node pgm [args]; execute node pgm
<#.md
.desc
executes the specified NodeJS script program and passes the args in as parameters.
.notes
Requires NodeJS to be installed.
#>
function node([string]$js) {
"node $js args=$($args.count) $($args[0..$args.count])"
$opts = $args[0..$args.count]
& "$($env:NODEJS_HOME)\node.exe" $js @opts
}
#@ easyType obj; display type of object
<#.md
.desc
A developer aid to display an object type.
#>
function easyType($obj) {
#param($obj)
if ($null -eq $obj) {return "NULL";}
$str = "$($obj.getType())";
$ix = $str.lastIndexOf(".");
if ($ix -gt 0) {return $str.substring($ix + 1,$str.length - $ix -1)}
return $str
}
#@ fav dir; switch to favorite dir. no param lists favorites.
<#.md
.desc
A system to allow symbolic references frequently used directory
#>
function fav($fav) {
$t = $CVT.favs;
if ($fav -eq $null) {
$t.GetEnumerator() | sort -Property name | forEach {
$dir = $_.value;
$name = $_.name;
"$($( $name ).PadRight(13) ) $( $dir )"
}
} else {
if (exists $t $fav) {
#hilite -yellow "$fav cd $($t[$fav])"
cdd $t[$fav]
} else {
hilite -yellow "$fav not defined as favorite"
}
}
}
#@ setFav name dir; set favorite and assoiated directory
<#.md
.desc
Adds a favorite / directory pair to the favorite hashMap
Typically called by the specific script run when PSEC-V4 starts.
#>
#>
#>
function setFav($name,$dir) {
$t = $CVT.favs;
$t[$name] = $dir;
hilite -yellow "setFav $name $dir"
}
#========================== end of commands =====================
function prepareDir([string]$leaf) {
if (test-path $leaf) {
$file = $leaf | Split-Path -Leaf
$dir = $leaf | Split-Path -Parent
[void]("startFile $file in $dir")
[void](& cdd $dir)
$appDir = [AppDir]::installAppDir($dir,$file);
[void]$CVT.addAppDir($appDir);
} else {
hilite -red "$($leaf) startFile does not exist"
}
return $file
}
function condMakeDir([string]$path) {
if (-not(Test-Path -Path $path)) {
[void](New-Item -ItemType Directory -Force -Path $path)
hilite -DarkYellow "Created directory $path)"
}
if (-not (isDir($path))) {exitFail("Directory $path not a directory. Cannot continue.")} # cannot continue
}
function exitFail([string]$why) {
hilite -red "$why"
exit
}
# This is a restart of the base window so that classes can be reloaded.
function swap([string]$opt) {
hlog("execute swap $opt")
# we add delay so that this process closes and we avoid a duplicate file.
$dlog = $GLOBAL:DLOG
$argList = @("-noprofile","-noexit","-file $($CVT.psecStart)","-delay 1","-dlog $($dlog.filePath)")
#$h.windowTitle = "$origTitle **busy**"
dlog("restarting with swap command");
flush("");
[void](start-process powershell.exe -argumentlist $argList)
exit #exit process, new window will start
}
#========================== Start point =====================
## prepareEnv
<#
The commands we parse are those ater the -file parameter which s/b the last
powershell parameter.
We are able to find and parse subsequent parameters.
#>
$argBias = 2
$runlogStr = "?"
$workDir = $env:PSEC_V4_WORK;
$jamDlog = "";
if ($NULL -eq $workDir) {
$workDir = "$pwd/temp";
if (-not (test-path $workDir)) {
exitFail("PSEC_V4_WORK environment variable needed to define workDir or /temp created in $pwd")
}
}
"workDir $workDir"
$CVT.locns.base = $CVT.psecBase;
$CVT.locns.work = $workDir;
$CVT.locns.dlog = "$workDir/dlog";
condMakeDir($CVT.locns.work);
condMakeDir($CVT.locns.dlog);
try {
$g = [GlobUtil]::new();
$CVT.gutil = $g
$ECID = (& ECID)
hilite -cyan "$reload ECID $ECID, use 'hh' or 'hh TYPE' to get show help"
if ($args[0] -eq '-startFile') {
if (test-path $args[1]) {
$file = prepareDir $args[1]
$CVT.run.cwd = $args[1]
$CVT.run.cwdStart = $file
hilite -green "installed $($CVT.run.cwdStart) in $($CVT.run.cwd) $file"
} else {
hilite -red "$($args[1]) startFile does not exist"
}
if (($args.count -ge 4) -and ($args[2] -eq '-theme')) {
# note - we do not check valid arg.3 as internally used or on shortcut
$CVT.iconTheme = $args[3]
$argBias += 2
hilite -green "installed theme $($CVT.iconTheme)"
cc $CVT.iconTheme
}
$runlogStr = $host.ui.RawUI.WindowTitle
} elseif ($args[0] -eq '-first') { #started from new command
# used by new command to create a sub-window that reloads classes
"run first $args arg0 $($args[0]) arg1 $($args[1]) sub $($args[2..$args.count])"
$iconTheme = 'cyn';
if (($args.count -ge 4) -and ($args[2] -eq '-theme')) {
# note - we do not check validity arg.3 as internally used or on shortcut
$iconTheme = $args[3]
$argBias += 2
##clear-host #EC0204
hilite -green "prepared theme $iconTheme"
}
<# if (test-path $args[1]) {
$file = prepareDir $args[1];
$CVT.run.cwd = $args[1]
$CVT.run.cwdStart = $file
$CVT.bSecCons = $true
$CVT.iconTheme = $iconTheme
hilite -green "REINSTALLED $($CVT.run.cwdStart) in $($CVT.psecBase) $($CVT.iconTheme) $($CVT.bSecCons)"
$hist = (import-csv "$($CVT.psecBase)\history.csv")
$hist | add-history;
$runlogStr = "PSEC Secondary: $($args[$argBias..$args.count])"
$host.ui.RawUI.WindowTitle = $runlogStr
##. ./scr/pos-window 100 100 #move window to side
##. cc $CVT.iconTheme
} else {
hilite -red "dir\start-file expected"
}#>
} elseif ($args[0] -eq '-delay') { #started from new command
# used by new command to create a sub-window that reloads classes
"run delay $args arg0 $($args[0]) arg1 $($args[1]) sub $($args[2..$args.count])"
start-sleep -seconds $($args[1])
"done with sleep"
$iconTheme = 'cyn';
if (($args.count -ge 4) -and ($args[2] -eq '-dlog')) {
# note - we do not check validity arg.3 as internally used or on shortcut
$jamDlog = $args[3]
$argBias += 2
##clear-host #EC0204
hilite -green "jamDlog $jamDlog"
}
<# if (test-path $args[1]) {
$file = prepareDir $args[1];
$CVT.run.cwd = $args[1]
$CVT.run.cwdStart = $file
$CVT.bSecCons = $true
$CVT.iconTheme = $iconTheme
hilite -green "REINSTALLED $($CVT.run.cwdStart) in $($CVT.psecBase) $($CVT.iconTheme) $($CVT.bSecCons)"
$hist = (import-csv "$($CVT.psecBase)\history.csv")
$hist | add-history;
$runlogStr = "PSEC Secondary: $($args[$argBias..$args.count])"
$host.ui.RawUI.WindowTitle = $runlogStr
##. ./scr/pos-window 100 100 #move window to side
##. cc $CVT.iconTheme
} else {
hilite -red "dir\start-file expected"
}#>
} else {
"default start up"
}
} catch {
$e = $_
hLogErr("$($e.exception.message)")
hLogErr("$($e.ScriptStackTrace)")
}
#EC9B14 - always load utilities
$utils = "$($CVT.psecBase)\lib\gui-utils.psm1"
import-module $utils -force
& utilsVersion "$utils"
#$dlogMod = "$($CVT.psecBase)\lib\dlog.psm1"
#import-module $dlogMod -force
#& dlogVersion "$dlogMod"
##$GLOBAL:DLOG = [Dlog]::create("d:/1/test-dlog.txt");
$GLOBAL:DLOG = [DLogObj]::create($CVT.locns.dlog,$jamDLog);
[string]$nowPic = (Get-Date -Format "ddd yyyy-MMM-dd HH:mm:ss")
$g.log("--- installation complete --- $nowPic $($CVT.iconTheme) $argBias $($args.count) $($args[0..$args.count])")
$GLOBAL:DLOG.append("PSEC-V4 window started at $nowPic");
runlog "psec-start $runlogStr"
##userNotify("starting PSEC");
if ($args.count -ge 0) { # run command
hilite -green "args.extra $($args.count) bias=$argBias 0=$($args[0]) 1=$($args[1]) 2=$($args[2]) 3=$($args[3])"
if ($args.count -eq 1) { # run command
& $args[0]
} else {
# had to use splat operator here
$opts = @();
forEach($opt in $args[($argBias + 1)..$args.count]) {
$opts += $opt;
}
# EC9302: For some reason the history does not take. pressing h shows it,
# retyping reinserts it. Might be something to do with typed history.
##& $args[$argBias] @opts
}
} else {
#hilite -green "RUN $($CVT.run.cwdStart) in $($CVT.run.cwd)"
#$arg1 = split-path $($CVT.run.cwd) -parent
#invoke-command -nonewscope -filepath "$($CVT.run.cwd)" -argumentlist (,$arg1)
#$CVT.run.cwdStart($CVT.run.cwd);
}
PSEC is an extensive set of Powershell enhancements that, along with other acknowledged works, is deployed into the public domain subject to the limitations detailed below.
Ackowledgements
Pandoc, the system used to produce this documentation.
More detailed information is found by clicking on the link that triggered this popup