From: Christian Goeschel Ndjomouo Date: Wed, 31 Dec 2025 23:56:22 +0000 (-0500) Subject: tools: verify the consistency of manpages for all programs X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0a9cc0ec611651c6dbcb8b825d491b2360411822;p=thirdparty%2Futil-linux.git tools: verify the consistency of manpages for all programs The tools/checkmanpage.sh script ensures that all programs have a corresponding manpage as well as the integrity of all described long options in the manpage based on the respective programs source code. Signed-off-by: Christian Goeschel Ndjomouo --- diff --git a/Makefile.am b/Makefile.am index 75bab88f4..7c07b63a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -338,6 +338,9 @@ checkdecl: checkcompletion: @ $(top_srcdir)/tools/checkcompletion.sh $(top_srcdir) +checkmanpage: + @ $(top_srcdir)/tools/checkmanpage.sh $(top_srcdir) + checkusage: @ $(top_srcdir)/tools/checkusage.sh $(top_srcdir) \ $(bin_PROGRAMS) $(sbin_PROGRAMS) \ diff --git a/tools/checkmanpage.sh b/tools/checkmanpage.sh new file mode 100755 index 000000000..948e7fff9 --- /dev/null +++ b/tools/checkmanpage.sh @@ -0,0 +1,142 @@ +#!/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 +# +# This script verifies if each program has a corresponding manpage +# and checks if said manpages have a description for each program's +# long options. The list of program long options is based on the +# respective program's source code. + +top_srcdir="${1:-.}" +if [ -d "${top_srcdir}" ]; then + shift 1 +else + echo "directory '${top_srcdir}' not found" >&2 + exit 1 +fi + +# These are programs that we do not need to check on +# swapoff: merged into the swapon manpage +ignore_programs="swapoff" + +# We only need manpages for sections 1,7 and 8 +manpages="$( find "$top_srcdir" -name "*[178].adoc" -type f | uniq )" + +# Extract all user-facing programs from Makemodule.am files +# We look for: bin_PROGRAMS, sbin_PROGRAMS, usrbin_exec_PROGRAMS, usrsbin_exec_PROGRAMS +function extract_programs() { + find "$top_srcdir" -name "Makemodule.am" -type f -exec grep -h \ + -E "^(bin|sbin|usrbin_exec|usrsbin_exec)_PROGRAMS \+=" {} \; \ + | sed 's/.*+= *//' \ + | tr ' ' '\n' \ + | sed 's/\\//' \ + | grep -v '^$' \ + | grep -v '\.static$' \ + | sort -u +} + +function find_prog_manpage() { + local prog + prog="$1" + + for m in ${manpages}; do + if [[ "${m##*/}" =~ ^"${prog}"\.[1-9]\.adoc$ ]]; then + echo "${m}" + break + fi + done +} + +function get_manpage_options() { + local adoc + adoc="$1" + + opts="$(cat "$adoc" \ + | grep -o -P '[[:space:]]*--(?![^[:alnum:]])[A-Za-z-.0-9_]*' \ + | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' \ + | sort \ + | uniq )" + + # --help and --version are included with a manpage stub @ man-common/help-version.adoc + if grep -E 'include::man-common\/help-version.adoc\[\]' "$adoc" &>/dev/null; then + opts="$(printf -- "--version\n--help\n%s" "$opts" | sort | uniq )" + fi + + # --annotate is included with a manpage stub @ man-common/annotate.adoc + if grep -E 'include::man-common\/annotate.adoc\[\]' "$adoc" &>/dev/null; then + opts="$(printf -- "--annotate\n%s" "$opts" | sort | uniq )" + fi + + # --hyperlink is included with a manpage stub @ man-common/hyperlink.adoc + if grep -E 'include::man-common\/hyperlink.adoc\[\]' "$adoc" &>/dev/null; then + opts="$(printf -- "--hyperlink\n%s" "$opts" | sort | uniq )" + fi + + echo "$opts" +} + +function check_manpage_integrity() { + local prog + prog="$1" + + adoc="$(find_prog_manpage "$prog")" + if [ -z "$adoc" ]; then + echo "ERROR: no manpage found for $prog" >&2 + return 1 + fi + + manpage_opts="$( get_manpage_options "$adoc" )" + + 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 + + res="$( comm -23 <(echo "${prog_long_opts}") <(echo "${manpage_opts}") )" + if [ -n "$res" ]; then + printf "%s\n%s\n" "${prog} (missing/extraneous manpage description(s)):" "$res" + return 1 + fi + + return 0 +} + +function main() { + programs="$(extract_programs)" + + errors=0 + + for p in $programs; do + [[ "$p" =~ $ignore_programs ]] && continue + check_manpage_integrity "$p" || errors=$((errors + 1)) + done + + exit $errors +} + +main