#!/usr/bin/perl -w

eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
    if 0; # not running under some shell
###############################################################################
# Sanity check plugin for the Krazy project.                                  #
# Copyright (C) 2006-2007,2009-2010 by Allen Winter <winter@kde.org>          #
# Copyright (C) 2006 by Jaison Lee <lee.jaison@gmail.com>                     #
#                                                                             #
# This program is free software; you can redistribute it and/or modify        #
# it under the terms of the GNU General Public License as published by        #
# the Free Software Foundation; either version 2 of the License, or           #
# (at your option) any later version.                                         #
#                                                                             #
# This program is distributed in the hope that it will be useful,             #
# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                #
# GNU General Public License for more details.                                #
#                                                                             #
# You should have received a copy of the GNU General Public License along     #
# with this program; if not, write to the Free Software Foundation, Inc.,     #
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.               #
#                                                                             #
###############################################################################

# Tests KDE source for QMIN and QMAX

# Program options:
#   --help:          print one-line help message and exit
#   --version:       print one-line version information and exit
#   --priority:      report issues of the specified priority only
#   --strict:        report issues with the specified strictness level only
#   --explain:       print an explanation with solving instructions
#   --installed      file is to be installed
#   --quiet:         suppress all output messages
#   --verbose:       print the offending content

# Exits with status=0 if test condition is not present in the source;
# else exits with the number of failures encountered.

use strict;
use FindBin qw($Bin);
use lib "$Bin/../../../../lib";
use Krazy::PreProcess;
use Krazy::Utils;

my($Prog) = "qminmax";
my($Version) = "1.5";

&parseArgs();

&Help() if &helpArg();
&Version() if &versionArg();
&Explain() if &explainArg();
if ($#ARGV != 0){ &Help(); Exit 0; }

my($f) = $ARGV[0];

# skip C source files
if ($f =~ m/\.c$/) {
  print "okay\n" if (!&quietArg());
  Exit 0;
}

#open file and slurp it in
open(F, "$f") || die "Couldn't open $f";
my(@data_lines) = <F>;
close(F);

# Remove C-style comments and #if 0 blocks from the file input
my(@lines) = RemoveIfZeroBlockC( RemoveCommentsC( @data_lines ) );

my($min_cnt) = 0;
my($max_cnt) = 0;
my($rmin_cnt) = 0;
my($rmax_cnt) = 0;
my($linecnt) = 0;
my($line);
my($min_str) = "";
my($max_str) = "";
my($rmin_str) = "";
my($rmax_str) = "";
foreach $line (@lines) {
  if ($line =~ m+//.*[Kk]razy:excludeall=.*$Prog+ ||
      $line =~ m+//.*[Kk]razy:skip+) {
    $min_cnt = $max_cnt = 0;
    last;
  }
  $linecnt++;
  next if ($line =~ m+//.*[Kk]razy:exclude=.*$Prog+);

  $line =~ s+//.*++;  #skip C++ comments

  #I'm going to cheat here. My regexps fail if QMIN/QMAX appear at the very
  #beginning or very end of the line. In c/c++ syntax this is highly unlikely,
  #but I can't rule it completely out. I'm sure there is SOME way of getting
  #this to work with ^ and $ but I can't do it reliably, so I just add a space
  #to the beginning and end of the line here and everything is happy and my
  #brains don't ooze out my ears from regexp overload.
  $line = " " . $line . " ";

  if ($line =~ m/[^[:alnum:]_]Q_?MIN[^[:alnum:]_]/ ) {
    $min_cnt++;
    if ($min_cnt == 1) {
      $min_str = "using QMIN line\#" . $linecnt;
    } else {
      $min_str = $min_str . "," . $linecnt;
    }
    print "=> $line" if (&verboseArg());
  }
  if ($line =~ m/[^[:alnum:]_]Q_?MAX[^[:alnum:]_]/ ) {
    $max_cnt++;
    if ($max_cnt == 1) {
      $max_str = "using QMAX line\#" . $linecnt;
    } else {
      $max_str = $max_str . "," . $linecnt;
    }
    print "=> $line" if (&verboseArg());
  }

  if ($line =~ m/qMax\s*\(\s*\d+\.\d*\s*,/ || $line =~ m/qMax\s*\(.*,\s*\d+\.\d*\s*\)/) {
    $rmax_cnt++;
    if ($rmax_cnt == 1) {
      $rmax_str = "passing non-casted float to qMax line\#" . $linecnt;
    } else {
      $rmax_str = $rmax_str . "," . $linecnt;
    }
    print "=> $line" if (&verboseArg());
  }
  if ($line =~ m/qMin\s*\(\s*\d+\.\d*\s*,/ || $line =~ m/qMin\s*\(.*,\s*\d+\.\d*\s*\)/) {
    $rmin_cnt++;
    if ($rmin_cnt == 1) {
      $rmin_str = "passing non-casted float to qMin line\#" . $linecnt;
    } else {
      $rmin_str = $rmin_str . "," . $linecnt;
    }
    print "=> $line" if (&verboseArg());
  }
}

my($total_count) = $min_cnt + $max_cnt + $rmin_cnt + $rmax_cnt;
if (!$total_count) {
  print "okay\n" if (!&quietArg());
  Exit 0;
} else {
  print "$min_str ($min_cnt)\n" if (!&quietArg() && $min_cnt);
  print "$max_str ($max_cnt)\n" if (!&quietArg() && $max_cnt);
  print "$rmin_str ($rmin_cnt)\n" if (!&quietArg() && $rmin_cnt);
  print "$rmax_str ($rmax_cnt)\n" if (!&quietArg() && $rmax_cnt);
  Exit $total_count;
}

sub Help {
  print "Check for QMIN and QMAX macros\n";
  Exit 0 if &helpArg();
}

sub Version {
  print "$Prog, version $Version\n";
  Exit 0 if &versionArg();
}

sub Explain {
  print "Obsolete macros QMIN(), Q_MIN(), QMAX() and Q_MAX() should be replaced by the qMin() and qMax() functions. Additionally, when passing floating point constants make sure to cast them as qreal's so the code builds ok on machines without an fpu (like ARMs). For example, qMax(foo(),1.0) should be changed to qMax(foo(),qreal(1.0))\n";
  Exit 0 if &explainArg();
}

