Utils/kill.ps1

Register-GnuUtil @{
    Name         = 'kill'
    Category     = 'Running commands'
    Synopsis     = 'Send a signal to a process.'
    Usage        = @('kill [-s SIGNAL | -SIGNAL] PID...', 'kill -l [SIGNAL]...', 'kill -t [SIGNAL]...')
    Description  = @('Send signals to processes, or list signals.')
    RawArguments = $true
    Options      = @(
        @{ Short = 's'; Long = 'signal'; Arg = 'SIGNAL'; Help = 'specify the name or number of the signal to be sent' }
        @{ Short = 'l'; Long = 'list'; Help = 'list signal names, or convert signal names to/from numbers' }
        @{ Short = 't'; Long = 'table'; Help = 'print a table of signal information' }
    )
    Notes        = @(
        'SIGNAL may be a signal name like ''HUP'', or a signal number like ''1'',',
        'or the exit status of a process terminated by a signal.',
        'PID is an integer; if negative it identifies a process group.',
        '',
        'PowerShell note: numbers are those of Linux. On Windows every signal terminates the',
        'process (with its children) and signal 0 only checks that it exists. Process groups',
        '(negative PIDs) are not supported.'
    )
}



function script:Invoke-GnuKillSignal {
    <#
    .SYNOPSIS
    Implementation of kill (its own argument parsing, because -SIGNAL and -NUMBER are options).
    #>

    param([Parameter(Mandatory)][hashtable]$Context)

    $arguments = [string[]]@($Context.Operands)
    $mode = 'send'
    $signalText = 'TERM'
    $index = 0
    while ($index -lt $arguments.Length) {
        $argument = $arguments[$index]
        if ($argument -ceq '--') { $index++; break }
        if ($argument -ceq '-l' -or $argument -ceq '--list') { $mode = 'list' }
        elseif ($argument -ceq '-t' -or $argument -ceq '--table' -or $argument -ceq '-L') { $mode = 'table' }
        elseif ($argument -ceq '-s' -or $argument -ceq '--signal') {
            $index++
            if ($index -ge $arguments.Length) { Write-GnuUsageError -Context $Context -Message "option requires an argument -- 's'"; return }
            $signalText = $arguments[$index]
        }
        elseif ($argument -cmatch '^--signal=(.*)$') { $signalText = $Matches[1] }
        elseif ($argument -cmatch '^-s(.+)$') { $signalText = $Matches[1] }
        elseif ($argument -cmatch '^-(\w+)$' -and $mode -eq 'send') { $signalText = $Matches[1] }
        else { break }
        $index++
    }
    $rest = @()
    if ($index -lt $arguments.Length) { $rest = $arguments[$index..($arguments.Length - 1)] }

    if ($mode -ne 'send') {
        $names = @($script:GnuSignals.Keys)
        $width = ($names | Measure-Object -Property Length -Maximum).Maximum
        if ($rest.Count -eq 0) {
            foreach ($name in $names) {
                if ($mode -eq 'list') { $name }
                else { '{0,2} {1} {2}' -f $script:GnuSignals[$name][0], $name.PadRight($width), $script:GnuSignals[$name][1] }
            }
            return
        }
        foreach ($item in $rest) {
            $lookup = $item
            if ($item -match '^\d+$' -and [int]$item -gt 128) { $lookup = [string]([int]$item - 128) }
            $signal = ConvertFrom-GnuSignal -Text $lookup
            if ($null -eq $signal -or $signal.Number -eq 0) { Write-GnuError -Context $Context -Message "'$item': invalid signal"; continue }
            if ($mode -eq 'table') { '{0,2} {1} {2}' -f $signal.Number, $signal.Name.PadRight($width), $script:GnuSignals[$signal.Name][1] }
            elseif ($item -match '^\d+$') { $signal.Name }
            else { [string]$signal.Number }
        }
        return
    }

    $signal = ConvertFrom-GnuSignal -Text $signalText
    if ($null -eq $signal) { Write-GnuError -Context $Context -Message "'$signalText': invalid signal"; return }
    if ($rest.Count -eq 0) { Write-GnuUsageError -Context $Context -Message 'no process ID specified'; return }
    foreach ($item in $rest) {
        $id = 0
        if (-not [int]::TryParse($item, [ref]$id)) { Write-GnuError -Context $Context -Message "'$item': invalid process id"; continue }
        if ($id -le 0) { Write-GnuError -Context $Context -Message "'$item': process groups are not supported"; continue }
        $reason = Send-GnuSignal -Id $id -Signal $signal
        if ($null -ne $reason) { Write-GnuError -Context $Context -Message "'$item': $reason" }
    }
}