1#!/usr/bin/perl -w
2#
3#  Gnumeric
4#
5#  Copyright (C) 2003 Morten Welinder.
6#
7#  This program is free software; you can redistribute it and/or
8#  modify it under the terms of the GNU General Public License as
9#  published by the Free Software Foundation; either version 2 of the
10#  License, or (at your option) any later version.
11#
12#  This library is distributed in the hope that it will be useful,
13#  but WITHOUT ANY WARRANTY; without even the implied warranty of
14#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15#  General Public License for more details.
16#
17#  You should have received a copy of the GNU General Public License
18#  along with this library; if not, see <https://www.gnu.org/licenses/>.
19#
20#  Author: Morten Welinder <terra@gnome.org>
21
22use strict;
23
24my $exitcode = 0;
25my $verbose = 0;
26my $strict = 0;
27
28warn "$0: should be run from top-level directory.\n"
29    unless -r "configure.ac" && -r 'ChangeLog';
30
31my %base_exceptions =
32    ();
33
34my %exceptions =
35    ();
36
37{
38    local (*FIND);
39    open (*FIND, "find . '(' -type f -name '*.c' -print ')' -o '(' -type d '(' -name .git -o -name intl -o -name macros -o -name win32 ')' -prune ')' |")
40	or die "$0: cannot execute find: $!\n";
41  FILE:
42    foreach my $filename (<FIND>) {
43	chomp $filename;
44	$filename =~ s|^\./||;
45
46	next if $exceptions{$filename};
47	my $basename = $filename;
48	$basename =~ s|^.*/||;
49	next if $base_exceptions{$basename};
50
51	local (*FIL);
52	if (open (*FIL, "< $filename")) {
53	    # print "Checking $filename...\n";
54	    my $lineno = 0;
55	    my $state = 0;
56	    my ($good,$dubious,$bad,$functype);
57	    my ($seen_good,$seen_dubious,$seen_bad);
58	    my %values;
59	  LINE:
60	    while (<FIL>) {
61		$lineno++;
62
63		if (/^(static\s+)?(inline\s+)?(unsigned\s+|signed\s+|const\s+)*(int|gu?int(|8|16|32|64)|char|gu?char)\b[^*]*$/) {
64		    $state = 1;
65		    $good = '[-+]?\d+';
66		    $dubious = 'FALSE|TRUE';
67		    $bad = 'NULL';
68		    $functype = 'integer';
69		} elsif (/^(static\s+)?(inline\s+)?gboolean\b[^*]*$/) {
70		    $state = 1;
71		    $good = 'FALSE|TRUE';
72		    $dubious = '0|1';
73		    $bad = 'NULL|[-+]\d+|[2-9]\d*';
74		    $functype = 'boolean';
75		} elsif (/^(static\s+)?(inline\s+)?(const\s+)?(gpointer|\S[ a-zA-Z0-9]+\*)/) {
76		    $state = 1;
77		    $good = 'NULL';
78		    $dubious = '0';
79		    $bad = 'FALSE';
80		    $functype = 'pointer';
81		}
82
83		if ($state == 1) {
84		    if (/;\s*$/) {
85			$state = 0;
86			next;
87		    }
88
89		    if (/^\{/) {
90			$state++;
91			$seen_good = $seen_dubious = $seen_bad = 0;
92			%values = ();
93			next;
94		    }
95		}
96
97		if (/^\}/) {
98		    if ($state == 2 && !$strict && $seen_good && $seen_dubious) {
99			print "$filename:$lineno: mixture of good and dubious return values: ";
100			print join (', ', sort keys %values), ".\n";
101		    }
102
103		    $state = 0;
104		    next;
105		}
106
107		if ($state == 2) {
108		    my $val;
109
110		    if (/\breturn(\s|\()\s*($good|$dubious|$bad)\s*\)*\s*;/) {
111			$val = $2;
112		    } elsif (/\bg_return_val_if_fail.*,\s*($good|$dubious|$bad)\s*\)\s*;\s*$/) {
113			$val = $1;
114		    } else {
115			next;
116		    }
117
118		    my $barf = 0;
119		    if ($val =~ /^($bad)$/) {
120			$seen_bad = 1;
121			$barf = 1;
122		    } elsif ($val =~ /^($dubious)$/) {
123			$seen_dubious = 1;
124			$barf = 1 if $strict;
125			$values{$val} = 1;
126		    } else {
127			$seen_good = 1;
128			$values{$val} = 1;
129		    }
130
131		    print "$filename:$lineno: returning $val in a $functype function.\n"
132			if $barf;
133
134		    next;
135		}
136	    }
137	    close (*FIL);
138	} else {
139	    print STDERR "$0: Cannot read `$filename': $!\b";
140	    $exitcode = 1;
141	}
142    }
143    close (*FIND);
144}
145
146exit $exitcode;
147