diff options
| author | Karel Zak <kzak@redhat.com> | 2025-12-03 11:14:13 +0100 |
|---|---|---|
| committer | Karel Zak <kzak@redhat.com> | 2025-12-03 11:14:13 +0100 |
| commit | 9bfd9d9262e9b42eb56f601945acbbe72a3c0220 (patch) | |
| tree | 2789c320e6b51e5c33558f492086c0fd0b3f017e | |
| parent | 764c97800eadd1a49225c566771fec6905115acb (diff) | |
| parent | c5c8326a8a19b7560b23fea386804c9f1325295b (diff) | |
| download | util-linux-9bfd9d9262e9b42eb56f601945acbbe72a3c0220.tar.gz | |
Merge branch 'feat/check_bash_comp_integrity' of https://github.com/cgoesche/util-linux-fork
* 'feat/check_bash_comp_integrity' of https://github.com/cgoesche/util-linux-fork:
bash-completion: (lslogins) fix typo in long option
tools: (checkcompletion.sh) test the integrity of long options completion
tools: new helper for extraction of program long options from source files
| -rw-r--r-- | bash-completion/lslogins | 2 | ||||
| -rwxr-xr-x | tools/checkcompletion.sh | 51 | ||||
| -rwxr-xr-x | tools/get-options.sh | 96 |
3 files changed, 147 insertions, 2 deletions
diff --git a/bash-completion/lslogins b/bash-completion/lslogins index efad4bc0b4..dd1770855d 100644 --- a/bash-completion/lslogins +++ b/bash-completion/lslogins @@ -73,7 +73,7 @@ _lslogins_module() --btmp-file --shell --lastlog-file - --lastlog-file2 + --lastlog2-file --help --version" -- $cur) ) return 0 diff --git a/tools/checkcompletion.sh b/tools/checkcompletion.sh index 58e1f7a08c..c92354a2c4 100755 --- a/tools/checkcompletion.sh +++ b/tools/checkcompletion.sh @@ -6,8 +6,10 @@ # 2. All bash-completion files are registered in bash-completion/Makemodule.am # 3. All bash-completion files are registered in meson.build # 4. All bash-completion files correspond to actual programs +# 5. All bash-completion files handle all available long options in each program # # Copyright (C) 2025 Karel Zak <kzak@redhat.com> +# Copyright (C) 2025 Christian Goeschel Ndjomouo <cgoesc2@wgu.edu> # set -e @@ -43,7 +45,11 @@ exclude_programs="nologin|agetty|login|sulogin|switch_root|vipw|line|kill" # These are handled via install-data-hook-bashcomp-* rules # - runuser: symlinked to su completion # - lastb: symlinked to last completion -special_handling="runuser|lastb" +special_handling="runuser|lastb" + +# Certain completions have an unusual algorithm that is distinct from the pattern used +# in the majority of completion files, we skip these for now. +unusual_completions="pipesz" top_srcdir=${1:-.} [ -d "${top_srcdir}" ] || die "directory '${top_srcdir}' not found" @@ -97,6 +103,44 @@ extract_meson_registered() { fi } +# Check for the bash-completion file integrity, i.e. all long options are completed +# Argument(s): program_name +check_completion_file_integrity() { + local prog="$1" + + prog_long_opts="$( TOP_SRCDIR="${top_srcdir}" "${top_srcdir}"/tools/get-options.sh "$prog" \ + | sed -e 's/^$//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + + if [[ "$?" != "0" || -z "$prog_long_opts" ]]; then + echo "Failed to get long options for $prog" + return 1 + fi + + # tools/get-options.sh prints 'ENOTSUP' when it receives the name of an + # unsupported program. See comments for the 'unsupported_programs' variable + # in tools/get-options.sh for more details. + # + # We do not treat this case as an error, thereby we simply return 0 to the + # caller and skip the comparison. + if [ "$prog_long_opts" == "ENOTSUP" ]; then + return 0 + fi + + comp_opts="$( cat "${completion_dir}/${prog}" \ + | grep -o -P '[[:space:]]*--(?![^[:alnum:]])[A-Za-z-.0-9_]*' \ + | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' \ + | sort \ + | uniq )" + + res="$( comm -23 <(echo "${prog_long_opts}") <(echo "${comp_opts}") )" + if [ -n "$res" ]; then + printf "%s\n%s\n" "${prog}:" "$res" + return 1 + fi + + return 0 +} + # Get programs that should have completions programs=$(extract_programs | grep -v -w -E "(${exclude_programs}|${special_handling})") @@ -148,6 +192,11 @@ if [ -n "$meson_unregistered" ]; then errors=$((errors + 1)) fi +for f in $files; do + [[ "$f" =~ $unusual_completions ]] && continue + check_completion_file_integrity "$f" || errors=$((errors + 1)) +done + if [ $errors -eq 0 ]; then echo "All bash-completion files are consistent." exit 0 diff --git a/tools/get-options.sh b/tools/get-options.sh new file mode 100755 index 0000000000..49d798ce7b --- /dev/null +++ b/tools/get-options.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# Copyright (C) 2025 Christian Goeschel Ndjomouo <cgoesc2@wgu.edu> +TOP_SRCDIR=${TOP_SRCDIR:-../} + +# Directories that contain relevant source files for util-linux programs. +src_file_paths="$(grep -rE --include="*.c" --exclude="*test_*" \ + --exclude-dir="lib*" \ + --exclude-dir="po*" \ + --exclude-dir="tests" \ + --exclude-dir="tools" \ + --exclude-dir="bash-completion" \ + --exclude-dir=".[a-z]*" \ + --exclude-dir="man-common" \ + --exclude-dir="Documentation" \ + --exclude-dir="build*" \ + -l "getopt_long(_only)?\s*\(" \ + )" + +# We skip these programs because they do not make use of 'struct option longopts[]' +# which is passed to getopt(3) for command line argument parsing. +unsupported_programs='blockdev|fsck|mkfs\.cramfs|pg|renice|whereis' + +# In general a program's source file name will be '<program_name>.c', however +# some tools have differing file names. To handle these special cases we build +# a hash table with the program name as the key and the actual source file name +# as the value, the latter will ultimately be passed to find_prog_src(). +typeset -A canonical_src_prefix +canonical_src_prefix=( \ + [su]="su-common" +) + +function find_prog_src() { + local prog + prog="$1" + + for p in ${src_file_paths}; do + if [[ "${p##*/}" =~ ^"${prog}".c$ ]]; then + echo "${TOP_SRCDIR}/${p}" + break + fi + done +} + +function extract_long_opts() { + local src_path + src_path="$1" + + awk -F ',' 'BEGIN { x = 0 }; \ + /struct[[:space:]]*option[[:space:]]*.*[[:space:]]*\[\][[:space:]]*=[[:space:]]*(\{)?/ { x = 1 } \ + x && ! /.*\/\*.*(deprecated|COMPLETION:no).*\*\/.*/ { print $1 } \ + /\};/ { x = 0 }' "${src_path}" \ + | grep -Eo '".*"' \ + | tr -d '"' \ + | sort \ + | awk '{ printf "--%s\n", $0 }' \ + | grep -v '^--_.*$' +} + +function main() { + local progname + progname="$1" + + if [[ "$progname" =~ $unsupported_programs ]]; then + echo "ENOTSUP" + return 0 + fi + + # Handle special programs that have unusual source file names + if [ -n "${canonical_src_prefix[$progname]+exists}" ]; then + progname="${canonical_src_prefix[$progname]}" + fi + + src_path="$(find_prog_src "$progname")" + if [ -z "$src_path" ]; then + return 1 + fi + + extract_long_opts "$src_path" + + return 0 +} + +main "$@" |
