diff options
-rw-r--r-- | bash-completion | 24 | ||||
-rwxr-xr-x | mkinitcpio | 19 |
2 files changed, 33 insertions, 10 deletions
diff --git a/bash-completion b/bash-completion index aa6efbb..597eab2 100644 --- a/bash-completion +++ b/bash-completion @@ -1,6 +1,14 @@ #!/bin/bash # mkinitcpio bash completion by Seblu <seblu@seblu.net> +detect_kver() { + local kver_validator='^[[:digit:]]+(\.[[:digit:]]+)+' + offset=$(hexdump -s 526 -n 2 -e '"%0d"' "$1" 2>/dev/null) || return 1 + read kver _ < \ + <(dd if="$1" bs=1 count=127 skip=$(( offset + 0x200 )) 2>/dev/null) + [[ $kver =~ $kver_validator ]] && printf "$kver" +} + _lsinitcpio() { local cur opts opts=(-a --analyze -c --config -h --help -l --list @@ -18,14 +26,14 @@ _lsinitcpio() { _find_kernel_versions() { local -a matches - local dir regex - - # add completions from kernels in /boot - regex="Linux kernel.*version" - while IFS=':' read -r file metadata; do - [[ $metadata =~ $regex ]] || continue - matches+=("$file") - done < <(file -e ascii /boot/*) + local dir f + + for f in /boot/*; do + # only match regular files which pass validation + if [[ ! -L $f && -f $f ]] && kver=$(detect_kver "$f"); then + matches+=("$f") + fi + done # add completions based on kernel versions in /lib/modules for dir in /lib/modules/*/kernel; do @@ -82,7 +82,13 @@ cleanup() { } resolve_kernver() { - local kernel=$1 + local kernel=$1 offset kver + + # this is intentionally very loose. only ensure that we're + # dealing with some sort of string that starts with something + # resembling dotted decimal notation. remember that there's no + # requirement for CONFIG_LOCALVERSION to be set. + local kver_validator='^[[:digit:]]+(\.[[:digit:]]+)+' if [[ -z $kernel ]]; then uname -r @@ -99,7 +105,16 @@ resolve_kernver() { return 1 fi - if file -Lb "$kernel" | grep -oP '(?<=version )[^ ]+'; then + # scrape the version out of the kernel image. locate the offset + # to the version string by reading 2 bytes out of image at at + # address 0x20E. this leads us to a string of, at most, 128 bytes. + # read the first word from this string as the kernel version. + offset=$(hexdump -s 526 -n 2 -e '"%0d"' "$kernel") + read kver _ < \ + <(dd if="$kernel" bs=1 count=127 skip=$(( offset + 0x200 )) 2>/dev/null) + + if [[ $kver =~ $kver_validator ]]; then + printf '%s' "$kver" return 0 fi |