Dictionaries/Dict.OS/Includes/Bootloader/Includes/Bootloader.refind.psm1


<#
.SYNOPSIS
Search for a refind configuration
 
.DESCRIPTION
Search into local partition for a refind.conf file.
Default search for a "refind.conf" file. If config name is overriden, use -Filename to search for custom filename.
 
.EXAMPLE
Find-OSBootloaderRefindCfg -Path "/boot"
 
.EXAMPLE
Find-OSBootloaderRefindCfg -Path "/boot" -Filename "myrefind.conf"
 
.NOTES
General notes
#>

function Find-OSBootloaderRefindCfg {
    [CmdletBinding()]
    [OutputType([String])]
    Param (
        # Path to start search
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$Path,
        # Custom filename to search for
        [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$Filename = "refind.conf",
        # Search as far as MaxDepth deep
        [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][uint32]$MaxDepth = 3
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        $filename = Get-ChildItem -Path $Path $Filename -Recurse -Depth $MaxDepth -ErrorAction SilentlyContinue
        return ${refindcfg}?.fullname
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Get refind boot order
 
.DESCRIPTION
according to the documentation @url https://www.rodsbooks.com/refind/configfile.html
 
the key is 'default_selection' from config file.
but it can appear several times.
 
.PARAMETER Filename
Parameter description
 
.PARAMETER Content
Parameter description
 
.EXAMPLE
An example
 
.NOTES
General notes
#>

function Get-OSBootloaderRefindBootOrder {
    [CmdletBinding()]
    [OutputType([String])]
    Param (
        [Alias('ConfigFile')]
        [Parameter(Mandatory = $false, ValueFromPipeLine = $false, ParameterSetName = 'FILENAME')][string]$Filename = "/boot/Refind/Refind.conf",
        [Parameter(Mandatory = $false, ValueFromPipeLine = $false, ParameterSetName = 'CONTENT')][array]$Content
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'FILENAME' {
                $Content = Get-OSBootloaderRefindArray -Filename $Filename
            }
        }
        # the key is 'default_selection' from config file.
        # but it can appear several times.
        # according to the help
    }

    End {
        Write-LeaveFunction
    }
}

function Set-OSBootloaderRefindDefaultBoot {
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    Param (
        [Alias('ConfigFile')]
        [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$Filename = "/boot/Refind/Refind.conf",
        [Parameter(Mandatory = $true, ValueFromPipeLine = $false)][string]$label
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        $Refind = (Get-Content $Filename -Raw) -replace "^DEFAULT .*", "DEFAULT $label"
        $Refind | Out-File $Filename
        return $?
    }

    End {
        Write-LeaveFunction
    }
}

function Get-OSBootloaderRefindObject {
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$Filename
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        $refind = @{}
        # $content = (Get-Content $Filename | Select-String -Pattern "^[a-zA-Z_0-9]+" -AllMatches | Select-String "menuentry" -NotMatch) -replace '(^[a-zA-Z_0-9]+)', '${1} = ' | ConvertFrom-StringData
        # foreach ($key in $content.keys) {
        # $refind.Add($key, $content.$key)
        # }
        # $content = (Get-Content $Filename | Select-String -Pattern "^[a-zA-Z_0-9]+" -AllMatches | Select-String "menuentry" -NotMatch) -replace '(^[a-zA-Z_0-9]+)', '${1} = ' | ConvertFrom-StringData
        foreach ($line in (Get-Content $Filename)) {
            if ($line -match "^(?<key>[a-zA-Z_0-9]+)\s(?<value>.*)") {
                $key = $matches.key
                $value = $matches.value
                switch ($key) {
                    'menuentry' { continue }
                    Default {
                        if ($refind.$key) {
                            if ($refind.$key -is [array]) {
                                $refind.$key += $value
                            } else {
                                $oldValue = $refind.$key
                                $refind.$key = @($oldValue, $value)
                            }
                        } else {
                            $refind.$key = $value
                        }
                    }
                }
            }
        }
        # $entries = (Get-Content -Raw $Filename) -split '\r?\n\r?\n' -match '^menuentry'
        # $menuentries = @()
        # foreach ($entry in $entries) {
        # $menuentry = @{}
        # # flatten menuentry
        # $flatEntry = (($entry -split '\n' -replace '^\s+') -replace '\\', '/' | Select-String "^#" -NotMatch -AllMatches) -replace ' {' -replace '}'
        # # convert to hashtable
        # $hash = $flatEntry -replace '(^[a-zA-Z_0-9]+)', '${1} = ' | ConvertFrom-StringData
        # foreach ($key in $hash.keys) {
        # $menuentry.Add($key, $hash.$key)
        # }
        # $menuentries += $menuentry
        # }

        $menuentries = @()
        $entries = Get-Content -Raw $Filename | Select-String -Pattern '(?ms)^menuentry .*?^}$' -AllMatches
        # edevel("MESSAGES = " + $MESSAGES.Matches[0])
        # Write-Debug "MESSAGE = $MESSAGE"
        foreach ($entry in $entries.Matches) {
            $menuEntry = @{}
            # save raw entry definition to lose nothing
            $menuEntry.raw = $entry.value
            if ($entry.value -match "^menuentry `"(?<label>.*?)`" .*") {
                $menuEntry.Label = $Matches.label
            }
            $options = $entry.value | Select-String -Pattern '\n\s+(?<k>[\S.]*)\s+(?<v>.*)' -AllMatches
            foreach ($option in $options.Matches) {
                if ($option.Groups['k'].Value -like "#*") {
                    # this is a comment
                    continue
                }
                $key = $option.Groups['k'].Value
                $value = $option.Groups['v'].Value
                if ($menuentry.$key) {
                    if ($menuentry.$key -is [array]) {
                        $menuentry.$key += $value
                    } else {
                        $oldValue = $menuentry.$key
                        $menuentry.$key = @($oldValue, $value)
                    }
                } else {
                    $menuentry.$key = $value
                }
            }
            $menuEntries += [PSCustomObject]$menuEntry
        }
        $refind.menuentries = $menuentries
        return [PSCustomObject]$refind
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Convert a refind.conf file into a usable object
 
.DESCRIPTION
Load a refind.conf file into an array. We can not convert to a hastable as keys can be declared multiple times.
'default_selection' for example or 'initrd'.
 
.EXAMPLE
Get-OSBootloaderRefindArray -ConfigurationFile /boot/EFI/refind/refind.conf
 
.NOTES
General notes
#>

function Get-OSBootloaderRefindArray {
    [CmdletBinding()]
    [OutputType([array])]
    Param (
        # Full path to refind.conf to load
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$Filename
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        $refind = @()
        # $content = (Get-Content $Filename | Select-String -Pattern "^[a-zA-Z_0-9]+" -AllMatches | Select-String "menuentry" -NotMatch) -replace '(^[a-zA-Z_0-9]+)', '${1} = ' | ConvertFrom-StringData
        # foreach ($key in $content.keys) {
        # $refind += @{$key = $content.$key}
        # }
        foreach ($line in (Get-Content $Filename)) {
            # # keep comments
            # if ($line -match "^#") {
            # $refind += $line
            # }

            if ($line -match "^(?<name>[a-zA-Z_0-9]+)\s(?<value>.*)") {
                switch ($matches.name) {
                    'menuentry' { continue }
                    Default {
                        $refind += @{$matches.name = $matches.value}
                    }
                }
            }
        }
        $entries = (Get-Content -Raw $Filename) -split '\r?\n\r?\n' -match '^menuentry'
        $menuentries = @()
        foreach ($entry in $entries) {
            $menuentry = @()
            foreach ($line in ($entry -split '\n' -replace '^\s+')) {
                if ($line -match "^(?<name>[a-zA-Z_0-9]+)\s(?<value>.*)") {
                    switch ($matches.name) {
                        'menuentry' {
                            $menuentry += @{$matches.name = $matches.value -replace ' {'}
                        }
                        Default {
                            $menuentry += @{$matches.name = $matches.value}
                        }
                    }
                }
            }
            $menuentries += @(, $menuentry)
        }

        $entries = Get-Content -Raw $Filename | Select-String -Pattern '(?ms)^menuentry .*?^}$' -AllMatches
        # edevel("MESSAGES = " + $MESSAGES.Matches[0])
        # Write-Debug "MESSAGE = $MESSAGE"
        foreach ($entry in $entries.Matches) {
            $menuEntry = @{}
            $menuEntry.Bootloader = "refind"
            $menuEntry.Filename = $Filename
            $options = $entry.value | Select-String -Pattern '\n\s+(?<k>[\S.]*)\s+(?<v>.*)' -AllMatches
            foreach ($option in $options.Matches) {
                $key = $option.Groups['k'].Value
                $value = $option.Groups['v'].Value
                if ($menuentry.$key) {
                    if ($menuentry.$key -is [array]) {
                        $menuentry.$key += $value
                    } else {
                        $oldValue = $menuentry.$key
                        $menuentry.$key = @($oldValue, $value)
                    }
                } else {
                    $menuentry.$key = $value
                }
            }
            $menuEntries += $menuEntry
        }

        $refind += @{"menuentries" = $menuentries}
        return $refind
    }

    End {
        Write-LeaveFunction
    }
}

function Get-OSBootloaderRefind {
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$Filename
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        [PSCustomObject]$refind = [PSCustomObject](Get-OSBootloaderRefindObject @PSBoundParameters)
        return $refind
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Convert a refind configuration array to a form writable to a file
 
.DESCRIPTION
Convert a previously loader configuration in a form suitable to be written again
 
.EXAMPLE
$content | Out-OSBootloaderRefindConf | Out-File /boot/EFI/refind/refind.conf
 
.NOTES
General notes
#>

function Out-OSBootloaderRefindConf {
    [CmdletBinding()]
    [OutputType([String])]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][array]$Content
    )
    Begin {
        Write-EnterFunction
        @"
#
# refind.conf
# Please read refind.conf-sample in the same directory to get explanations
# or consult official help @url https://www.rodsbooks.com/refind/configfile.html
#
"@

    }

    Process {

        for ($i = 0; $i -lt $Content.Count; $i++) {
            foreach ($key in $Content[$i].keys) {
                switch ($key) {
                    'menuentries' {
                        foreach ($menuentry in $Content.menuentries) {
                            ""
                            for ($j = 0; $j -lt $menuentry.Count; $j++) {
                                foreach ($mkey in $menuentry[$j].keys) {
                                    switch ($mkey) {
                                        'menuentry' {
                                            ""
                                            "menuentry $($menuentry[$j].$mkey) {"
                                        }
                                        Default {
                                            " $mkey $($menuentry[$j].$mkey)"
                                        }
                                    }
                                }
                            }
                            "}"
                        }
                    }
                    Default {
                        "$key $($Content[$i].$key)"
                    }
                }
            }
        }
    }

    End {
        ""
        Write-LeaveFunction
    }
}

function Get-OSBootloaderRefindBootEntries {
    # [CmdletBinding()]
    # [OutputType([String])]
    # Param (
    # # Custom filename to search on
    # [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$Filename,

    # # Show all entries label
    # [Parameter(Mandatory = $false, ParameterSetName = 'ALL')][switch]$All
    # )
    # Begin {
    # Write-EnterFunction
    # }

    # Process {
    # $menuEntries = @()
    # # $submenuEntries = @()
    # if (Test-FileExist $Filename) {
    # # parse grub.cfg
    # # To extract correct §, we need to read the file with -Raw parameter
    # # (?ms) sets regex options m (treats ^ and $ as line anchors) and s (makes . match \n (newlines) too`.
    # # ^## .*? matches any line starting with ## and any subsequent characters *non-greedily* (non-greedy is '.*?' set of characters at the end of pattern).
    # # -AllMatches to get... well... all matches
    # $content = Get-Content -Raw $Filename | Select-String -Pattern '(?ms)^menuentry .*?^}$' -AllMatches
    # # edevel("MESSAGES = " + $MESSAGES.Matches[0])
    # # Write-Debug "MESSAGE = $MESSAGE"
    # foreach ($msg in $content.Matches) {
    # $menuEntry = [PSCustomObject]@{}
    # $menuEntry | Add-Member -MemberType NoteProperty -name "Bootloader" -Value "refind"
    # $menuEntry | Add-Member -MemberType NoteProperty -name "Filename" -Value $Filename
    # if ($msg.value -match "^menuentry `"(?<label>.*?)`" .*") {
    # $menuEntry | Add-Member -MemberType NoteProperty -name "Label" -Value $Matches.label
    # }
    # if ($msg.value -match "`n\s*loader\s*(?<kernel>.*)") {
    # $menuEntry | Add-Member -MemberType NoteProperty -name "kernel" -Value $Matches.kernel
    # }
    # if ($msg.value -match "`n\s*initrd\s*(?<initrd>.*)") {
    # $menuEntry | Add-Member -MemberType NoteProperty -name "initrd" -Value $Matches.initrd
    # }
    # if ($msg.value -match "`n\s*options\s*(?<options>.*)") {
    # $menuEntry | Add-Member -MemberType NoteProperty -name "options" -Value $Matches.options
    # }
    # $menuEntries += $menuEntry
    # }
    # } else {
    # return $null
    # }

    # return $menuEntries
    # }

    # End {
    # Write-LeaveFunction
    # }
}