#!/usr/bin/perl $USAGE = " USAGE: $0 options dir SYNOPSIS: Find common problem / ambiguities in C programs: Recursively scans *.c *.h *.cpp files under dir. OPTIONS: -? Help -v Verbose. -W[0-9] Warn level. -l Log warns in c-check.log -lLOGFILE Log warns in LOGFILE -f Search all files (ie. =~ .*). -fPATTERN Search only files matching PATTERN. -Ffilelist Scan only files listed in filelist. NOTES: . Assignments inside: asserts(x=1), if, while. . Backslash followed by trailing spaces. . Wrong variables in: for(iap=1;iap<10;jap++){ ... } . Some Precedence Ambiguities. eg. (a< $logfile" ) || die "Cannot write log to $logfile\n"; } while ( $file = ){ chop $file; print STDERR "FILE: $file.\n" if $verbose; $total_files++; next unless $filelist || $file =~ m/$filepat/i; next if( $skipfiles && ( $file =~ m/$skipfiles/i )); next if( $skipdirs && ( $file =~ m/$skipdirs/i )); next unless -T $file; checkfile( $file ); } print STDERR "Scanned $scanned_files of $total_files files.\n"; print STDERR "Examined $total_lines lines with $total_warns warnings\n"; print STDERR "In $warn_files files.\n" if $warn_files; if( $logfile ){ print STDERR "Wrote $logfile.\n"; close( LOG ); } # ==================================================================== # Returns number of lines scanned. sub checkfile { local ($file ) = @_ ; if( ! open( SRCFILE, $file ) ){ print STDERR "Cannot open $file.\n"; return 0; } $scanned_files++; print STDERR "[$scanned_files] Checking file $file.\n" if $verbose; while(){ $warn = 0; $error =''; s,//.*,, ; if( m/\bassert\b.*[^!<>=]=[^=]/ ){ # assert(i=1) $warn++; $error = 'assert='; } if( m/\b(if|while)\s* \(.*[^!<>=]=[^=]\w+[^\w()]\) /x # if(i=1) && ! m/\+\+|--|==/ # if( c=p++) ok. && ! m/\(.*\w+\s*\(.*\)\)/ # if(c=get(x)) ok. && !m/\(.*\)\s*{?}?\n/ # if(c=2){ ok. } && !m/return/ # if(1) return; ok. ){ $warn++; $error = 'cond='; } # To handle do{ .. }\n while( cond ); if( m/^\s*(if)\s*\(.*\)\s*;\s*$/ # if( x==1 ) # print x if $verbose; ok. # {...}while( Cond ); ok. && !m/return/ # if(x) return; && ! m/--|\+\+/ # while(*p++) ok. && !m/\(.*\).*\w+\s*\(.*\)\s*;/ # if(x) abort(x); ok. && !m/[)]\s*[(]/ # if(x) (f=y) ok. ){ $warn++; $error = 'cond has no body'; $print_following_lines = 1; } if( m/\\s+$/ ){ # Backslash space newline. $warn++; $error = 'backslash spaces'; } # for loop bugs, test: for(iap=1;iap<10;jap++){... } if( ($warn_level > 4) && m/ for \s* \( \s* ([^;]*,)* ([\w_]+) \s* = \s* [^;\s]+\s*; \s* ([\w_]+) \s* [<>]=* \s* ([^;\s]+) \s*; \s* ([\w_]+) [+-]{2,2} \s* \) /x && ( ($2 ne $3 ) || ($2 ne $5) ) ){ $warn++; $error = "loop vars($2!=$3 or $2!=$5)"; } # if( m/\bsizeof\(?\s*(lp\w*)/i && ($badsize = $1) && # ($badsize !~ m/lpstr/i ) && # ($badsize !~ m/lpvoid/i ) && # ($badsize !~ m/lpbyte/i ) # ){ # $warn++; # $error = "sizeof pointer $badsize"; # } # Trivial ambiguities. # if( m/\*\w+\.\w+/ ){ # *p.f is *(p.f) # $warn++; # $error = '?.'; # } # if( m/\*\w+\[.*\]/ ){ # *a[] is *(a[]) # $warn++; # $error = '?[]'; # } # if( m/\*\w+\(.*\)/ ){ # *f() is *(f()) # $warn++; # $error = '?*'; # } if( m/\w+\s*\&\s*\w+\s*[=!]=/ ){ # a&b ==c is a&(b==c) $warn++; $error = '?=='; } if( m/\w+\s*(<<|>>)\s*[+-]\s*\w/ ){ # a< 0 ){ if( m/\S/ ){ print "\t $_"; print LOG "\t $_" if $logfile; --$print_following_lines; } } if( eof ){ print STDERR "[$scanned_files] file: $file had $. lines.\n" if $verbose; $total_lines += $.; close(SRCFILE); return $.; } } } # ====================================================================