1*2dbc4ebcSMarkus Armbruster#!/usr/bin/perl -w 2*2dbc4ebcSMarkus Armbruster# 3*2dbc4ebcSMarkus Armbruster# Clean up include guards in headers 4*2dbc4ebcSMarkus Armbruster# 5*2dbc4ebcSMarkus Armbruster# Copyright (C) 2016 Red Hat, Inc. 6*2dbc4ebcSMarkus Armbruster# 7*2dbc4ebcSMarkus Armbruster# Authors: 8*2dbc4ebcSMarkus Armbruster# Markus Armbruster <armbru@redhat.com> 9*2dbc4ebcSMarkus Armbruster# 10*2dbc4ebcSMarkus Armbruster# This work is licensed under the terms of the GNU GPL, version 2 or 11*2dbc4ebcSMarkus Armbruster# (at your option) any later version. See the COPYING file in the 12*2dbc4ebcSMarkus Armbruster# top-level directory. 13*2dbc4ebcSMarkus Armbruster# 14*2dbc4ebcSMarkus Armbruster# Usage: scripts/clean-header-guards.pl [OPTION]... [FILE]... 15*2dbc4ebcSMarkus Armbruster# -c CC Use a compiler other than cc 16*2dbc4ebcSMarkus Armbruster# -n Suppress actual cleanup 17*2dbc4ebcSMarkus Armbruster# -v Show which files are cleaned up, and which are skipped 18*2dbc4ebcSMarkus Armbruster# 19*2dbc4ebcSMarkus Armbruster# Does the following: 20*2dbc4ebcSMarkus Armbruster# - Header files without a recognizable header guard are skipped. 21*2dbc4ebcSMarkus Armbruster# - Clean up any untidy header guards in-place. Warn if the cleanup 22*2dbc4ebcSMarkus Armbruster# renames guard symbols, and explain how to find occurences of these 23*2dbc4ebcSMarkus Armbruster# symbols that may have to be updated manually. 24*2dbc4ebcSMarkus Armbruster# - Warn about duplicate header guard symbols. To make full use of 25*2dbc4ebcSMarkus Armbruster# this warning, you should clean up *all* headers in one run. 26*2dbc4ebcSMarkus Armbruster# - Warn when preprocessing a header with its guard symbol defined 27*2dbc4ebcSMarkus Armbruster# produces anything but whitespace. The preprocessor is run like 28*2dbc4ebcSMarkus Armbruster# "cc -E -DGUARD_H -c -P -", and fed the test program on stdin. 29*2dbc4ebcSMarkus Armbruster 30*2dbc4ebcSMarkus Armbrusteruse strict; 31*2dbc4ebcSMarkus Armbrusteruse Getopt::Std; 32*2dbc4ebcSMarkus Armbruster 33*2dbc4ebcSMarkus Armbruster# Stuff we don't want to clean because we import it into our tree: 34*2dbc4ebcSMarkus Armbrustermy $exclude = qr,^(disas/libvixl/|include/standard-headers/ 35*2dbc4ebcSMarkus Armbruster |linux-headers/|pc-bios/|tests/tcg/|tests/multiboot/),x; 36*2dbc4ebcSMarkus Armbruster# Stuff that is expected to fail the preprocessing test: 37*2dbc4ebcSMarkus Armbrustermy $exclude_cpp = qr,^include/libdecnumber/decNumberLocal.h,; 38*2dbc4ebcSMarkus Armbruster 39*2dbc4ebcSMarkus Armbrustermy %guarded = (); 40*2dbc4ebcSMarkus Armbrustermy %old_guard = (); 41*2dbc4ebcSMarkus Armbruster 42*2dbc4ebcSMarkus Armbrusterour $opt_c = "cc"; 43*2dbc4ebcSMarkus Armbrusterour $opt_n = 0; 44*2dbc4ebcSMarkus Armbrusterour $opt_v = 0; 45*2dbc4ebcSMarkus Armbrustergetopts("c:nv"); 46*2dbc4ebcSMarkus Armbruster 47*2dbc4ebcSMarkus Armbrustersub skipping { 48*2dbc4ebcSMarkus Armbruster my ($fname, $msg, $line1, $line2) = @_; 49*2dbc4ebcSMarkus Armbruster 50*2dbc4ebcSMarkus Armbruster return if !$opt_v or $fname =~ $exclude; 51*2dbc4ebcSMarkus Armbruster print "$fname skipped: $msg\n"; 52*2dbc4ebcSMarkus Armbruster print " $line1" if defined $line1; 53*2dbc4ebcSMarkus Armbruster print " $line2" if defined $line2; 54*2dbc4ebcSMarkus Armbruster} 55*2dbc4ebcSMarkus Armbruster 56*2dbc4ebcSMarkus Armbrustersub gripe { 57*2dbc4ebcSMarkus Armbruster my ($fname, $msg) = @_; 58*2dbc4ebcSMarkus Armbruster return if $fname =~ $exclude; 59*2dbc4ebcSMarkus Armbruster print STDERR "$fname: warning: $msg\n"; 60*2dbc4ebcSMarkus Armbruster} 61*2dbc4ebcSMarkus Armbruster 62*2dbc4ebcSMarkus Armbrustersub slurp { 63*2dbc4ebcSMarkus Armbruster my ($fname) = @_; 64*2dbc4ebcSMarkus Armbruster local $/; # slurp 65*2dbc4ebcSMarkus Armbruster open(my $in, "<", $fname) 66*2dbc4ebcSMarkus Armbruster or die "can't open $fname for reading: $!"; 67*2dbc4ebcSMarkus Armbruster return <$in>; 68*2dbc4ebcSMarkus Armbruster} 69*2dbc4ebcSMarkus Armbruster 70*2dbc4ebcSMarkus Armbrustersub unslurp { 71*2dbc4ebcSMarkus Armbruster my ($fname, $contents) = @_; 72*2dbc4ebcSMarkus Armbruster open (my $out, ">", $fname) 73*2dbc4ebcSMarkus Armbruster or die "can't open $fname for writing: $!"; 74*2dbc4ebcSMarkus Armbruster print $out $contents 75*2dbc4ebcSMarkus Armbruster or die "error writing $fname: $!"; 76*2dbc4ebcSMarkus Armbruster close $out 77*2dbc4ebcSMarkus Armbruster or die "error writing $fname: $!"; 78*2dbc4ebcSMarkus Armbruster} 79*2dbc4ebcSMarkus Armbruster 80*2dbc4ebcSMarkus Armbrustersub fname2guard { 81*2dbc4ebcSMarkus Armbruster my ($fname) = @_; 82*2dbc4ebcSMarkus Armbruster $fname =~ tr/a-z/A-Z/; 83*2dbc4ebcSMarkus Armbruster $fname =~ tr/A-Z0-9/_/cs; 84*2dbc4ebcSMarkus Armbruster return $fname; 85*2dbc4ebcSMarkus Armbruster} 86*2dbc4ebcSMarkus Armbruster 87*2dbc4ebcSMarkus Armbrustersub preprocess { 88*2dbc4ebcSMarkus Armbruster my ($fname, $guard) = @_; 89*2dbc4ebcSMarkus Armbruster 90*2dbc4ebcSMarkus Armbruster open(my $pipe, "-|", "$opt_c -E -D$guard -c -P - <$fname") 91*2dbc4ebcSMarkus Armbruster or die "can't run $opt_c: $!"; 92*2dbc4ebcSMarkus Armbruster while (<$pipe>) { 93*2dbc4ebcSMarkus Armbruster if ($_ =~ /\S/) { 94*2dbc4ebcSMarkus Armbruster gripe($fname, "not blank after preprocessing"); 95*2dbc4ebcSMarkus Armbruster last; 96*2dbc4ebcSMarkus Armbruster } 97*2dbc4ebcSMarkus Armbruster } 98*2dbc4ebcSMarkus Armbruster close $pipe 99*2dbc4ebcSMarkus Armbruster or gripe($fname, "preprocessing failed ($opt_c exit status $?)"); 100*2dbc4ebcSMarkus Armbruster} 101*2dbc4ebcSMarkus Armbruster 102*2dbc4ebcSMarkus Armbrusterfor my $fname (@ARGV) { 103*2dbc4ebcSMarkus Armbruster my $text = slurp($fname); 104*2dbc4ebcSMarkus Armbruster 105*2dbc4ebcSMarkus Armbruster $text =~ m,\A(\s*\n|\s*//\N*\n|\s*/\*.*?\*/\s*\n)*|,msg; 106*2dbc4ebcSMarkus Armbruster my $pre = $&; 107*2dbc4ebcSMarkus Armbruster unless ($text =~ /\G(.*\n)/g) { 108*2dbc4ebcSMarkus Armbruster $text =~ /\G.*/; 109*2dbc4ebcSMarkus Armbruster skipping($fname, "no recognizable header guard", "$&\n"); 110*2dbc4ebcSMarkus Armbruster next; 111*2dbc4ebcSMarkus Armbruster } 112*2dbc4ebcSMarkus Armbruster my $line1 = $1; 113*2dbc4ebcSMarkus Armbruster unless ($text =~ /\G(.*\n)/g) { 114*2dbc4ebcSMarkus Armbruster $text =~ /\G.*/; 115*2dbc4ebcSMarkus Armbruster skipping($fname, "no recognizable header guard", "$&\n"); 116*2dbc4ebcSMarkus Armbruster next; 117*2dbc4ebcSMarkus Armbruster } 118*2dbc4ebcSMarkus Armbruster my $line2 = $1; 119*2dbc4ebcSMarkus Armbruster my $body = substr($text, pos($text)); 120*2dbc4ebcSMarkus Armbruster 121*2dbc4ebcSMarkus Armbruster unless ($line1 =~ /^\s*\#\s*(if\s*\!\s*defined(\s*\()?|ifndef)\s* 122*2dbc4ebcSMarkus Armbruster ([A-Za-z0-9_]+)/x) { 123*2dbc4ebcSMarkus Armbruster skipping($fname, "no recognizable header guard", $line1, $line2); 124*2dbc4ebcSMarkus Armbruster next; 125*2dbc4ebcSMarkus Armbruster } 126*2dbc4ebcSMarkus Armbruster my $guard = $3; 127*2dbc4ebcSMarkus Armbruster unless ($line2 =~ /^\s*\#\s*define\s+([A-Za-z0-9_]+)/) { 128*2dbc4ebcSMarkus Armbruster skipping($fname, "no recognizable header guard", $line1, $line2); 129*2dbc4ebcSMarkus Armbruster next; 130*2dbc4ebcSMarkus Armbruster } 131*2dbc4ebcSMarkus Armbruster my $guard2 = $1; 132*2dbc4ebcSMarkus Armbruster unless ($guard2 eq $guard) { 133*2dbc4ebcSMarkus Armbruster skipping($fname, "mismatched header guard ($guard vs. $guard2) ", 134*2dbc4ebcSMarkus Armbruster $line1, $line2); 135*2dbc4ebcSMarkus Armbruster next; 136*2dbc4ebcSMarkus Armbruster } 137*2dbc4ebcSMarkus Armbruster 138*2dbc4ebcSMarkus Armbruster unless ($body =~ m,\A((.*\n)*) 139*2dbc4ebcSMarkus Armbruster (\s*\#\s*endif\s*(/\*\s*.*\s*\*/\s*)?\n?) 140*2dbc4ebcSMarkus Armbruster (\n|\s)*\Z,x) { 141*2dbc4ebcSMarkus Armbruster skipping($fname, "can't find end of header guard"); 142*2dbc4ebcSMarkus Armbruster next; 143*2dbc4ebcSMarkus Armbruster } 144*2dbc4ebcSMarkus Armbruster $body = $1; 145*2dbc4ebcSMarkus Armbruster my $line3 = $3; 146*2dbc4ebcSMarkus Armbruster my $endif_comment = $4; 147*2dbc4ebcSMarkus Armbruster 148*2dbc4ebcSMarkus Armbruster my $oldg = $guard; 149*2dbc4ebcSMarkus Armbruster 150*2dbc4ebcSMarkus Armbruster unless ($fname =~ $exclude) { 151*2dbc4ebcSMarkus Armbruster my @issues = (); 152*2dbc4ebcSMarkus Armbruster $guard =~ tr/a-z/A-Z/ 153*2dbc4ebcSMarkus Armbruster and push @issues, "contains lowercase letters"; 154*2dbc4ebcSMarkus Armbruster $guard =~ s/^_+// 155*2dbc4ebcSMarkus Armbruster and push @issues, "is a reserved identifier"; 156*2dbc4ebcSMarkus Armbruster $guard =~ s/(_H)?_*$/_H/ 157*2dbc4ebcSMarkus Armbruster and $& ne "_H" and push @issues, "doesn't end with _H"; 158*2dbc4ebcSMarkus Armbruster unless ($guard =~ /^[A-Z][A-Z0-9_]*_H/) { 159*2dbc4ebcSMarkus Armbruster skipping($fname, "can't clean up odd guard symbol $oldg\n", 160*2dbc4ebcSMarkus Armbruster $line1, $line2); 161*2dbc4ebcSMarkus Armbruster next; 162*2dbc4ebcSMarkus Armbruster } 163*2dbc4ebcSMarkus Armbruster 164*2dbc4ebcSMarkus Armbruster my $exp = fname2guard($fname =~ s,.*/,,r); 165*2dbc4ebcSMarkus Armbruster unless ($guard =~ /\Q$exp\E\Z/) { 166*2dbc4ebcSMarkus Armbruster $guard = fname2guard($fname =~ s,^include/,,r); 167*2dbc4ebcSMarkus Armbruster push @issues, "doesn't match the file name"; 168*2dbc4ebcSMarkus Armbruster } 169*2dbc4ebcSMarkus Armbruster if (@issues and $opt_v) { 170*2dbc4ebcSMarkus Armbruster print "$fname guard $oldg needs cleanup:\n ", 171*2dbc4ebcSMarkus Armbruster join(", ", @issues), "\n"; 172*2dbc4ebcSMarkus Armbruster } 173*2dbc4ebcSMarkus Armbruster } 174*2dbc4ebcSMarkus Armbruster 175*2dbc4ebcSMarkus Armbruster $old_guard{$guard} = $oldg 176*2dbc4ebcSMarkus Armbruster if $guard ne $oldg; 177*2dbc4ebcSMarkus Armbruster 178*2dbc4ebcSMarkus Armbruster if (exists $guarded{$guard}) { 179*2dbc4ebcSMarkus Armbruster gripe($fname, "guard $guard also used by $guarded{$guard}"); 180*2dbc4ebcSMarkus Armbruster } else { 181*2dbc4ebcSMarkus Armbruster $guarded{$guard} = $fname; 182*2dbc4ebcSMarkus Armbruster } 183*2dbc4ebcSMarkus Armbruster 184*2dbc4ebcSMarkus Armbruster unless ($fname =~ $exclude) { 185*2dbc4ebcSMarkus Armbruster my $newl1 = "#ifndef $guard\n"; 186*2dbc4ebcSMarkus Armbruster my $newl2 = "#define $guard\n"; 187*2dbc4ebcSMarkus Armbruster my $newl3 = "#endif\n"; 188*2dbc4ebcSMarkus Armbruster $newl3 =~ s,\Z, /* $guard */, if defined $endif_comment; 189*2dbc4ebcSMarkus Armbruster if ($line1 ne $newl1 or $line2 ne $newl2 or $line3 ne $newl3) { 190*2dbc4ebcSMarkus Armbruster $pre =~ s/\n*\Z/\n\n/ if $pre =~ /\N/; 191*2dbc4ebcSMarkus Armbruster $body =~ s/\A\n*/\n/; 192*2dbc4ebcSMarkus Armbruster if ($opt_n) { 193*2dbc4ebcSMarkus Armbruster print "$fname would be cleaned up\n" if $opt_v; 194*2dbc4ebcSMarkus Armbruster } else { 195*2dbc4ebcSMarkus Armbruster unslurp($fname, "$pre$newl1$newl2$body$newl3"); 196*2dbc4ebcSMarkus Armbruster print "$fname cleaned up\n" if $opt_v; 197*2dbc4ebcSMarkus Armbruster } 198*2dbc4ebcSMarkus Armbruster } 199*2dbc4ebcSMarkus Armbruster } 200*2dbc4ebcSMarkus Armbruster 201*2dbc4ebcSMarkus Armbruster preprocess($fname, $opt_n ? $oldg : $guard) 202*2dbc4ebcSMarkus Armbruster unless $fname =~ $exclude or $fname =~ $exclude_cpp; 203*2dbc4ebcSMarkus Armbruster} 204*2dbc4ebcSMarkus Armbruster 205*2dbc4ebcSMarkus Armbrusterif (%old_guard) { 206*2dbc4ebcSMarkus Armbruster print STDERR "warning: guard symbol renaming may break things\n"; 207*2dbc4ebcSMarkus Armbruster for my $guard (sort keys %old_guard) { 208*2dbc4ebcSMarkus Armbruster print STDERR " $old_guard{$guard} -> $guard\n"; 209*2dbc4ebcSMarkus Armbruster } 210*2dbc4ebcSMarkus Armbruster print STDERR "To find uses that may have to be updated try:\n"; 211*2dbc4ebcSMarkus Armbruster print STDERR " git grep -Ew '", join("|", sort values %old_guard), 212*2dbc4ebcSMarkus Armbruster "'\n"; 213*2dbc4ebcSMarkus Armbruster} 214