1#!/usr/bin/env perl 2# SPDX-License-Identifier: GPL-2.0 3 4use warnings; 5use strict; 6 7my $srctree = shift @ARGV; 8unless (defined $srctree) { 9 $srctree = `git rev-parse --show-toplevel 2>/dev/null`; 10 chomp $srctree; 11 my $msg = "Usage: $0 <srctree> [excludes file]\n"; 12 $msg .= "Please provide <srctree>."; 13 $msg .= " Is it '$srctree'?" if $srctree; 14 $msg .= "\n"; 15 die $msg; 16} 17my $kconfig_sym_check_excludes = defined $ARGV[0] ? $ARGV[0] : undef; 18 19sub indent_depth { 20 my ($ws) = @_; 21 my $col = 0; 22 for my $c (split //, $ws) { 23 $col = $c eq "\t" ? int($col / 8) * 8 + 8 : $col + 1; 24 } 25 return $col; 26} 27 28my @files = `git -C \Q$srctree\E ls-files '*Kconfig*' 2>/dev/null`; 29if (@files) { 30 chomp @files; 31 @files = map { "$srctree/$_" } @files; 32} else { 33 @files = `find \Q$srctree\E -name '*Kconfig*'`; 34 chomp @files; 35} 36 37@files = grep { !m{/scripts/kconfig/tests/} } @files; 38 39my %configs = (); 40my %refs = (); 41 42foreach my $file (@files) { 43 open F, $file or die "Cannot open $file: $!"; 44 45 my $help = 0; 46 my $help_level; 47 my $level; 48 49 while (<F>) { 50 chomp; 51 52 while (/\\\s*$/) { 53 s/\\\s*$/ /; 54 my $cont = <F> // last; 55 chomp $cont; 56 $_ .= $cont; 57 } 58 59 next if /^\s*$/; 60 next if /^\s*#/; 61 62 /^(\s*)/; 63 $level = indent_depth($1); 64 65 if ($help && $level < $help_level) { 66 $help = 0; 67 } 68 69 next if ($help); 70 71 if (/^\s*(help|\-\-\-help\-\-\-)$/) { 72 $help = 1; 73 my $next; 74 while (defined($next = <F>)) { 75 last unless $next =~ /^\s*(?:#.*)?$/; 76 } 77 last unless defined $next; 78 $next =~ /^(\s*)/; 79 if (indent_depth($1) >= $level) { 80 $help_level = indent_depth($1); 81 } else { 82 $help = 0; 83 } 84 $_ = $next; 85 redo; 86 } 87 88 if (/^\s*(config|menuconfig)\s+([a-zA-Z0-9_]+)\s*(#.*)?$/) { 89 $configs{$2}++; 90 next; 91 } 92 93 if (/^\s*(default|def_bool|def_tristate|select|depends\s+on|imply|visible\s+if|range|if|bool|tristate|int|hex|string|prompt)\s+(.+)\s*$/) { 94 my $s = $2; 95 $s =~ s/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'//g; 96 $s =~ s/#.*//; 97 $s =~ s/\$\((?:[^()]*|\((?:[^()]*|\([^()]*\))*\))*\)//g; 98 $s =~ s/%%[^%]*%%//g; 99 my @syms = split /[^a-zA-Z0-9_]+/, $s; 100 map { 101 $refs{$_}++ if (/[a-zA-Z]/ && $_ ne "if" && $_ ne "y" && $_ ne "n" && $_ ne "m" && !/^0[xX][0-9a-fA-F]+$/); 102 } @syms 103 } 104 } 105 106 close F; 107} 108 109my %known_syms = (); 110if (defined $kconfig_sym_check_excludes) { 111 my $file = $kconfig_sym_check_excludes; 112 open(F, "<", $file) or die "Cannot open $file: $!"; 113 while (<F>) { 114 chomp; 115 next if /^\s*$/; 116 next if /^\s*#/; 117 $known_syms{$1}++ if (/^\s*([a-zA-Z0-9_]+)\s*(#.*)?$/); 118 } 119} 120 121my $ret = 0; 122foreach my $k (sort keys %refs) { 123 next if (exists $configs{$k} || exists $known_syms{$k}); 124 125 print "$k"; 126 print " - warning: '$k' is probably not what you want; Kconfig tristate literals are always lowercase ('n', 'y', 'm')" if ($k eq "N" || $k eq "Y" || $k eq "M"); 127 print "\n"; 128 129 $ret = 1; 130} 131 132exit $ret; 133