File: check-manpages-completions.pl

package info (click to toggle)
mpich 4.3.0%2Breally4.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 419,120 kB
  • sloc: ansic: 1,215,557; cpp: 74,755; javascript: 40,763; f90: 20,649; sh: 18,463; xml: 14,418; python: 14,397; perl: 13,772; makefile: 9,279; fortran: 8,063; java: 4,553; asm: 324; ruby: 176; lisp: 19; php: 8; sed: 4
file content (201 lines) | stat: -rwxr-xr-x 5,905 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env perl
#
# Copyright © 2021 Inria.  All rights reserved.
# $COPYRIGHT$
#
#
# For any option found in usage (either .c (C file) or .in (Shell script))
# check that they appear in the manpage (dash escaped) and completions.
#
# By default, added lines in git diff HEAD are considered for listing options.
# -a enables all options in usage.
#

use strict;
use Getopt::Long;
use Cwd;

my $verbose = 0;
my $checkalllines = 0;
my $help = 0;

GetOptions(
    "verbose" => \$verbose,
    "all" => \$checkalllines,
    "help" => \$help,
) or die "Unrecognized options.";

if ($help) {
    print <<EOT;
$0 [options]

--all | -a           Check all lines, not only added ones
--verbose | -v       Verbose messages
--help | -h          Show this help message
EOT
    exit(0);
}

# Go to the root of the GIT clone
while (! -e ".git") {
    chdir("..");
    die "Can't find root of the GIT clone" if (cwd() eq "/");
}

# Load the completion file only once
my $completion_file = "contrib/completion/bash/hwloc";
open(COMPL, $completion_file) or die "couldn't open completions $completion_file: $!";
my @completion_lines = <COMPL>;
close COMPL;

# If >1, we found some missing options
my $missing = 0;

# Check that one option is in the manpage
sub check_manpage {
    my ($file, $_option) = @_;

    # escape dashes
    my $option = $_option;
    $option =~ s/-/\\\\-/g;

    open(MAN, $file)
	or die "couldn't open manpage $file: $!";

    my $found = 0;
    while (<MAN>) {
	if ($_ =~ /$option[ =\\]/) { # option followed by space, equal or \fR
	    $found = 1;
	    last;
	}
    }
    close MAN;

    if (!$found) {
	print "  Failed to find $_option in manpage $file\n";
	$missing++;
    }
}

# Extract the completion function for one program
sub find_completion_func {
    my ($func, $option) = @_;

    my $start = undef;
    my $end = undef;
    for(my $i=0;; $i++) {
	if ($completion_lines[$i] =~ /^$func\(\)\s*\{$/) {
	    $start = $i+1;
	} elsif ($completion_lines[$i] =~ /^\}/ and defined $start) {
	    $end = $i-1;
	    last;
	}
    }
    die "Failed to find beginning of completion function $func in $completion_file"
	unless defined $start;
    die "Failed to find end of completion function $func in $completion_file"
	unless defined $end;
    return @completion_lines[$start..$end];
}

# Check that one option is in the completion function
sub check_completion {
    my ($func, $option, @lines) = @_;
    if (!grep { /$option/ } @lines) {
	print "  Failed to find $option in completion function $func in $completion_file\n";
	$missing++;
    }
}

# Check one program
sub check_file {
    my ($source_file, $manpage_file, $completion_func) = @_;

    if ($verbose) {
	print "Checking changes in $source_file for manpage $manpage_file and completion function $completion_func\n";
    }

    # C or Shell file?
    my $is_c_file = 0;
    $is_c_file = 1 if $source_file =~ /\.c$/;
    my $is_shell_file = 0;
    $is_shell_file = 1  if $source_file =~ /\.in$/;
    die "Source file $source_file is neither C nor shell?" unless $is_shell_file + $is_c_file == 1;

    my @lines;
    if ($checkalllines) {
	# Read the entire file
	open (SOURCE, $source_file) or die "Couldn't open source $source_file: $!";
	@lines = <SOURCE>;
	close SOURCE;
    } else {
	# Only read added lines
	open(SOURCE, "git diff HEAD -- $source_file |") or die "Couldn't open git diff of $source_file: $!";
	while (<SOURCE>) {
	    my $line = $_;
	    next unless $line =~ /^\+/;
	    $line =~ s/^\+//;
	    push @lines, $line;
	}
	close SOURCE;
    }

    # Only keep usage lines
    if ($is_c_file) {
	# fprintf(where, "  -...
	my $pat = qr/^\s+fprintf\s*\(where,\s*"\s+-/;
	@lines = grep { /$pat/ } @lines;
    } elsif ($is_shell_file) {
	# echo "  -...
	my $pat = qr/^\s+echo\s+"\s+-/;
	@lines = grep { /$pat/ } @lines;
    }

    # Extract options from those lines
    my @options = ();
    foreach my $line (@lines) {
	my @tokens = split /\s+/, $line;
	foreach my $token (@tokens) {
	    next unless $token =~ /^-/;
	    $token =~ s/[=\[\"\\].*//; # stop after '=' (mandatory arg) '[' (optional arg) or '\' (\n) or '"' (multiple lines)
	    push @options, $token;
	}
    }

    # Extract the completion function for this program
    my @complines;
    if (defined $completion_func) {
	@complines = find_completion_func ($completion_func);
	return unless @complines;
    }

    # Check every option
    foreach my $option (@options) {
	if ($verbose) {
	    print " Looking for option $option ...\n";
	}
	check_manpage ($manpage_file, $option);
	if (defined $completion_func) {
	    check_completion ($completion_func, $option, @complines);
	}
    }
}

check_file ("utils/lstopo/lstopo.c", "utils/lstopo/lstopo-no-graphics.1in", "_lstopo");
check_file ("utils/hwloc/hwloc-annotate.c", "utils/hwloc/hwloc-annotate.1in", "_hwloc_annotate");
check_file ("utils/hwloc/hwloc-bind.c", "utils/hwloc/hwloc-bind.1in", "_hwloc_bind");
check_file ("utils/hwloc/hwloc-calc.c", "utils/hwloc/hwloc-calc.1in", "_hwloc_calc");
check_file ("utils/hwloc/hwloc-diff.c", "utils/hwloc/hwloc-diff.1in", "_hwloc_diff");
check_file ("utils/hwloc/hwloc-distrib.c", "utils/hwloc/hwloc-distrib.1in", "_hwloc_distrib");
check_file ("utils/hwloc/hwloc-dump-hwdata.c", "utils/hwloc/hwloc-dump-hwdata.1in"); # no completion
check_file ("utils/hwloc/hwloc-gather-cpuid.c", "utils/hwloc/hwloc-gather-cpuid.1in", "_hwloc_gather_cpuid");
check_file ("utils/hwloc/hwloc-info.c", "utils/hwloc/hwloc-info.1in", "_hwloc_info");
check_file ("utils/hwloc/hwloc-patch.c", "utils/hwloc/hwloc-patch.1in", "_hwloc_patch");
check_file ("utils/hwloc/hwloc-ps.c", "utils/hwloc/hwloc-ps.1in", "_hwloc_ps");

check_file ("utils/hwloc/hwloc-gather-topology.in", "utils/hwloc/hwloc-gather-topology.1in", "_hwloc_gather_topology");
check_file ("utils/hwloc/hwloc-compress-dir.in", "utils/hwloc/hwloc-compress-dir.1in", "_hwloc_compress_dir");

exit 1 if $missing;

exit 0