#!/usr/bin/perl

use strict;
use warnings;

my $RE_Ignore = qr!^(COPYING|install-sh|config\.(sub|guess)|configure|(.*/)?Make.*|(doc/nasl|lib)/.*)$!;

my $Error = 0;
my $Last_Empty = 0;

# Check for presence of required include files.

my %Requires =
    (# C++ Standard Library
     qr/std::string/    => '<string>',
     qr/std::vector/    => '<vector>',
     qr/std::map/       => '<map>',
     qr/std::auto_ptr/  => '<memory>',

     # C Library
     qr/std(in|out|err)\b/ => '<cstdio>',
     qr/printf/         => '<cstdio>',

     # POSIX
     qr/getpid/         => '<unistd.h>',
     );


sub check_includes ($) {
    my $File = shift;

    open FILE, "< $File";

    my %Sys_Includes;

    while (my $Line = <FILE>) {
        chomp $Line;
        if ($Line =~ /^\s*\#include/) {
            if ($Line =~ /^\#include ([\"<][a-z0-9\/_]+(?:\.h)?[\">])/) {
                my $Include = $1;
                if ($Include =~ /^<.*>/) {
                    # Only system include files are tracked.
                    $Sys_Includes{$Include}++;
                }
            } else {
                print "$File:$.: invalid #include directive\n";
                $Error++;
            }
        }

        for my $RE (keys %Requires) {
            if ($Line =~ $RE) {
                unless ($Sys_Includes{$Requires{$RE}}) {
                    print "$File:$.: include file $Requires{$RE} required\n";
                    # Report only once.
                    $Sys_Includes{$Requires{$RE}}++;
                    $Error++;
                }
            }
        }
    }

    close FILE;
}

# Generic checks for tabs and whitespace.

for my $File (@ARGV) {
    if ($File =~ $RE_Ignore) {
        next;
    }

    open FILE, "< $File"
        or die "$File: open failed: $!\n";
    while (my $Line = <FILE>) {
        if ($Line =~ /\n$/) {
            chomp $Line;
        } else {
            print "$File:$.: line does not end with whitespace\n";
            $Error++;
        }

        if ($Line eq "\x0c") {
            next;
        }

        if ($Line =~ /\s+$/) {
            print "$File:$.: trailing whitespace\n";
            $Error++;
        }

        if ($Line =~ /\t/) {
            print "$File:$.: tab character\n";
            $Error++;
        }

        $Last_Empty = $Line =~ /^\s*$/;
    }

    if ($Last_Empty) {
        print "$File:$.: trailing empty lines\n";
    }

    close FILE;

    if ($File =~ /\.(cc|h)/) {
        check_includes $File;
    }
}

if ($Error) {
    exit 1;
} else {
    exit 0;
}
