]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
tools: new helper for extraction of program long options from source files
authorcgoesche <cgoesc2@wgu.edu>
Fri, 31 Oct 2025 05:58:11 +0000 (01:58 -0400)
committerChristian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Thu, 27 Nov 2025 20:40:11 +0000 (15:40 -0500)
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
tools/checkcompletion.sh
tools/get-options.sh [new file with mode: 0755]

index 58e1f7a08c78df4aef803101e8db0afb12b9810c..c815c1c43e81147789b1d7df9dc9da287361d3dd 100755 (executable)
@@ -97,6 +97,35 @@ 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"
+
+       if "./$prog" --version &>/dev/null; then
+               prog_long_opts="$( "./$prog" --help \
+                       | grep -o -P '[[:space:]]*--(?![^[:alpha:]])[A-Za-z-]*' \
+                       | sed -e 's/^ *//' \
+                       -e 's/ *$//' \
+                       | sort \
+                       | uniq )"
+
+               comp_opts="$( cat "${completion_dir}/${prog}" \
+                               | grep -o -P '[[:space:]]*--(?![^[:alpha:]])[A-Za-z-]*' \
+                               | 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
+       fi
+
+       return 0
+}
+
 # Get programs that should have completions
 programs=$(extract_programs | grep -v -w -E "(${exclude_programs}|${special_handling})")
 
@@ -148,6 +177,12 @@ if [ -n "$meson_unregistered" ]; then
        errors=$((errors + 1))
 fi
 
+if [ $errors -eq 0 ]; then
+       for f in $files; do
+               check_completion_file_integrity "$f" || errors=$((errors + 1))
+       done
+fi
+
 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 (executable)
index 0000000..49d798c
--- /dev/null
@@ -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 "$@"