source: branches/port5/SOURCE_TOOLS/check_ressources.pl

Last change on this file was 5887, checked in by westram, 16 years ago
  • added resource tester (make ressource_check)
  • Each ARB application has one default icon now
    • if icon is missing, no fallback exists - instead the application raises an error
    • all windows created under one AW_root use the same icon
    • it's still possible to define special icons for special windows (but it's not used atm)
  • class AW_window
    • create_toggle now expects that names of toggle-graphics start with '#' (previously it accepted with and without '#')
    • moved code from set_icon to aw_create_shell
    • added code setting small titlebar/taskbar-icon. Works in GNOME (please report whether it works in KDE as well)
    • .xpm possible for icons
  • class AW_root
    • renamed init() to init_root() to make function name unique for resource testing. Removed default-value for 2nd parameter
    • init_root() stores 'programmname' in class member (used as name for default icon)
  • fixed/removed calls to changed/removed functions
  • moved all unused resources into 'unused' subfolders
  • Property svn:executable set to *
File size: 15.6 KB
Line 
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5# use diagnostics;
6
7# --------------------------------------------------------------------------------
8
9my $ARBHOME = $ENV{ARBHOME};
10if (not defined $ARBHOME) { die "Environmentvariable ARBHOME has be defined"; }
11if (not -d $ARBHOME) { die "ARBHOME ('$ARBHOME') does not point to a valid directory"; }
12
13# --------------------------------------------------------------------------------
14
15my @pictures  = (); # contains all .fig
16my @pixmaps   = (); # contains all .bitmap, .xpm
17my @helpfiles = (); # contains all .help, .pdf, .pdf.gz, .ps, .ps.gz
18
19my %known   = (); # contains all files contained in arrays above
20my %unknown = (); # contains all other files found in scanned directories
21
22my %picture  = (); # key=subdir/name (as used in code), value=index into @pictures
23my %pixmap   = (); # key=subdir/name (as used in code), value=index into @pixmaps
24my %helpfile = (); # key=subdir/name (as used in code), value=index into @helpfiles
25
26my %used = (); # key=file, value=1 -> used in code
27
28my %full2rel = (); # key=full ressource, value=relative ressource (w/o rootdir)
29my %rel2full = (); # opposite
30
31# --------------------------------------------------------------------------------
32
33sub scanFiles(\@$$$$);
34sub scanFiles(\@$$$$) {
35  my ($files_r,$dir,$mask,$recurse,$ignoreLinks) = @_;
36
37  my $reg = qr/$mask/;
38
39  my @subdirs = ();
40  opendir(DIR,$dir) || die "can't read directory '$dir' (Reason: $!)";
41  foreach (readdir(DIR)) {
42    if ($_ ne '.' and $_ ne '..') {
43      my $full = $dir.'/'.$_;
44      if (-d $full) {
45        if (/unused/o) {
46          print "Ignoring ressource directory '$full' (assuming it contains unused things)\n";
47        }
48        elsif (not (/^.svn$/o or /^CVS$/o)) {
49          push @subdirs, $full;
50        }
51      }
52      else {
53        if ($ignoreLinks==0 || not -l $full) {
54          if ($_ =~ $reg) {
55            push @$files_r, $full;
56            $known{$full} = 1;
57            if (defined $unknown{$full}) { delete $unknown{$full}; }
58          }
59          elsif (/^Makefile$/) {
60            ;                   # ignore
61          }
62          elsif (not defined $known{$full}) {
63            $unknown{$full} = 1;
64          }
65        }
66      }
67    }
68  }
69  closedir(DIR);
70
71  if ($recurse==1) {
72    foreach (@subdirs) {
73      scanFiles(@$files_r, $_, $mask, $recurse, $ignoreLinks);
74    }
75  }
76}
77
78sub scanFilesAndIndex(\%\@$$$$) {
79  my ($index_r,$files_r,$dir,$mask,$recurse,$ignoreLinks) = @_;
80  scanFiles(@$files_r,$dir,$mask,$recurse,$ignoreLinks);
81
82  my $len   = length($dir)+1; # plus '/'
83  my $count = scalar(@$files_r);
84  for (my $c=0; $c<$count; $c++) {
85    my $rel = substr($$files_r[$c], $len);
86    $$index_r{$rel} = $c;
87    # print "full='".$$files_r[$c]."' idx='$c' rel='$rel'\n";
88  }
89}
90
91sub scanExistingRessources() {
92  scanFilesAndIndex(%picture,  @pictures,  $ARBHOME.'/lib/pictures',        '.*\.(fig|vfont)$',                         1, 0);
93  scanFilesAndIndex(%pixmap,   @pixmaps,   $ARBHOME.'/lib/pixmaps',         '.*\.(bitmap|xpm)$',                1, 0);
94  # scanFilesAndIndex(%helpfile, @helpfiles, $ARBHOME.'/HELP_SOURCE/oldhelp', '.*\.(hlp|ps|pdf|ps\.gz|pdf\.gz)$', 1, 0);
95
96  foreach (sort keys %unknown) {
97    if (/readme[^\/]*$/i) {
98      ; # ignore readme files
99    }
100    else {
101      print "$_:0: Unhandled file in ressource directory\n";
102    }
103  }
104
105  foreach (keys %picture)  { my $full = $pictures [$picture{$_}];  $full2rel{$full} = $_; }
106  foreach (keys %pixmap)   { my $full = $pixmaps  [$pixmap{$_}];   $full2rel{$full} = $_; }
107  foreach (keys %helpfile) { my $full = $helpfiles[$helpfile{$_}]; $full2rel{$full} = $_; }
108
109  foreach (keys %full2rel) { $rel2full{$full2rel{$_}} = $_; }
110}
111
112# --------------------------------------------------------------------------------
113
114my $reg_parser = qr/([\(\),\\\"\'\;\{]|\/\*|\/\/)/;
115my $reg_parse_dquotes = qr/(\\.|\")/;
116my $reg_parse_squotes = qr/(\\.|\')/;
117my $reg_parse_eoc = qr/\*\//;
118
119sub scanNextToken(\$\@\$);
120sub scanNextToken(\$\@\$) {
121  my ($rest_r,$file_r,$lineNr_r) = @_;
122  # scans for the next token (tokens are '(', ')', ',', ';' and '{')
123  # and returns it together with it's prefix
124  # modifies rest_r and lineNr_r
125  # if no token is found until EOF, token will be 'undef'
126  # reads over comments
127
128  my $prefix = '';
129  my $match = undef;
130  if ($$rest_r =~ $reg_parser) {
131    my ($preTok,$tok) = ($`,$&);
132    $$rest_r = $';
133
134    if ($tok eq '(' or $tok eq ')' or $tok eq ',' or $tok eq ';' or $tok eq '{') { # accept wanted tokens
135      $prefix .= $preTok;
136      $match = $tok;
137    }
138    elsif ($tok eq '\\') { # skip escaped chars
139      $prefix .= $preTok.$tok.substr($$rest_r,0,1);
140      $$rest_r = substr($$rest_r,1);
141    }
142    elsif ($tok eq '/*') { # skip /**/-comments
143      $prefix .= $preTok;
144      # print "prefix='$prefix' preTok='$preTok' rest_r='$$rest_r'\n";
145      my $found = 0;
146      while ($found==0) {
147        if ($$rest_r =~ $reg_parse_eoc) {
148          # print "\$`='$`' \$&='$&' \$'='$''\n";
149          if (not $& eq '*/') { die "expected to see '*/', parsed '$&' from '$$rest_r' (this is a bug in check_ressources.pl!)"; }
150          $$rest_r = $';
151          $found = 1;
152        }
153        else {
154          $$rest_r = $$file_r[$$lineNr_r++];
155          chomp($$rest_r);
156          # print "Continue in next line (while searching '*/'): '$$rest_r'\n";
157          if (not defined $$rest_r) { die "Unclosed '/*'"; }
158        }
159      }
160    }
161    elsif ($tok eq '//') {
162      $prefix .= $preTok;
163      $$rest_r = $$file_r[$$lineNr_r++];
164      chomp($$rest_r);
165      # print "Continue in next line (skipping '//'): '$$rest_r'\n";
166    }
167    elsif ($tok eq '"') {
168      $prefix .= $preTok.$tok;
169      my $closed_dquote = 0;
170      while ($closed_dquote==0) {
171        if ($$rest_r =~ $reg_parse_dquotes) {
172          $prefix .= $`.$&;
173          $$rest_r = $';
174          if ($& eq '"') { $closed_dquote = 1; }
175        }
176        else { die "Missing '\"'"; }
177      }
178    }
179    elsif ($tok eq '\'') {
180      $prefix .= $preTok.$tok;
181      my $closed_squote = 0;
182      while ($closed_squote==0) {
183        if ($$rest_r =~ $reg_parse_squotes) {
184          $prefix .= $`.$&;
185          $$rest_r = $';
186          if ($& eq '\'') { $closed_squote = 1; }
187        }
188        else { die "Missing '\''"; }
189      }
190    }
191    else {
192      die "Unknown token '$tok'";
193    }
194
195    if (not defined $match) { # continue
196      my $nextPrefix;
197      ($nextPrefix,$match) = scanNextToken($$rest_r, @$file_r, $$lineNr_r);
198      $prefix .= $nextPrefix;
199    }
200  }
201  else {
202    $prefix .= $$rest_r;
203    $$rest_r = $$file_r[$$lineNr_r++];
204    chomp($$rest_r);
205    # print "Continue in next line: '$$rest_r'\n";
206    if (defined $$rest_r) { # not EOF yet
207      my $p;
208      ($p,$match) = scanNextToken($$rest_r,@$file_r,$$lineNr_r);
209      $prefix .= $p;
210    }
211  }
212
213  return ($prefix,$match);
214}
215
216sub scanParams($\@$\$) {
217  my ($rest,$file_r,$lineNr,$calltype_r) = @_;
218
219  $$calltype_r = 0; # no params
220
221  my ($prefix,$token) = scanNextToken($rest,@$file_r,$lineNr);
222  my @params = ();
223  if ($token eq '(') {
224    if (trim($prefix) ne '') {
225      # print "Found prefix '$prefix' before potential parameter list - assume it's sth else\n";
226    }
227    else {
228      my $openParens = 1;
229      my $prevPrefix = '';
230      while ($openParens>0) {
231        ($prefix,$token) = scanNextToken($rest,@$file_r,$lineNr);
232        my $seen_eop = 0;
233
234        if (not defined $token) { die "EOF reached while scanning parameter list"; }
235        elsif ($token eq ')') { $openParens--; if ($openParens==0) { $seen_eop = 1; } }
236        elsif ($token eq '(') { $openParens++; }
237        elsif ($token eq ',') { if ($openParens==1) { $seen_eop = 1; } }
238        else { die "Unexpected token '$token' (behind '$prefix')"; }
239
240        $prevPrefix .= $prefix;
241        if ($seen_eop==1) { push @params, $prevPrefix; $prevPrefix = ''; }
242        else { $prevPrefix .= $token; }
243      }
244
245      $$calltype_r = 1;
246      ($prefix,$token) = scanNextToken($rest,@$file_r,$lineNr);
247      if ($token eq ';') {
248        $$calltype_r = 2;
249      }
250      elsif ($token eq '{') {
251        $$calltype_r = 3;
252      }
253      else {
254        print "unknown token behind call: '$token'\n";
255      }
256    }
257  }
258
259  return @params;
260}
261
262sub trim($) {
263  my ($str) = @_;
264  $str =~ s/^\s+//g;
265  $str =~ s/\s+$//g;
266  return $str;
267}
268
269sub isQuoted($) {
270  my ($str) = @_;
271  if ($str =~ /^\"(.*)\"$/) { return $1; }
272  return undef;
273}
274
275# --------------------------------------------------------------------------------
276
277sub acceptAll($) {
278  my ($res_param) = @_;
279  return ($res_param);
280}
281
282sub isPGTres($) {
283  my ($res_param) = @_;
284  return ('pgt/'.$res_param);
285}
286
287sub isBitmapRef($) {
288  my ($res_param) = @_;
289  if ($res_param =~ /^#/) { return ($'); }
290  return ();
291}
292
293sub isIconRes($) {
294  my ($res_param) = @_;
295  my $base = 'icons/'.$res_param;
296  return ($base.'.xpm', $base.'.bitmap');
297}
298
299# sub acceptExistingIconRes($) {
300#   my ($res_param) = @_;
301#   my $base = 'icons/'.$res_param;
302#   my @res = ();
303#   my $pm = $base.'.xpm'; if (defined $pixmap{$pm}) { push @res, $pm; }
304#   $pm = $base.'.bitmap'; if (defined $pixmap{$pm}) { push @res, $pm; }
305#   return @res;
306# }
307
308my @defs =
309  (
310   # regexp for function,       param numbers, expectInIndex, isRessource,
311   [ qr/\bload_xfig\b/,         [ 1 ],         \%picture,     \&acceptAll,     ],
312   [ qr/\bcreate_toggle\b/,     [ -2, -3 ],    \%pixmap,      \&isBitmapRef,   ],
313   [ qr/\binsert_toggle\b/,     [ 1 ],         \%pixmap,      \&isBitmapRef,   ],
314   [ qr/\bcreate_button\b/,     [ 2 ],         \%pixmap,      \&isBitmapRef,   ],
315   [ qr/\bcreate_mode\b/,       [ 2 ],         \%pixmap,      \&acceptAll,     ],
316   [ qr/\bAWMIMT\b/,            [ 2 ],         \%pixmap,      \&isBitmapRef,   ],
317   [ qr/\binsert_menu_topic\b/, [ 2 ],         \%pixmap,      \&isBitmapRef,   ],
318   [ qr/\bPGT_LoadPixmap\b/,    [ 1 ],         \%pixmap,      \&isPGTres,      ],
319   [ qr/\binit_root\b/,         [ 1 ],         \%pixmap,      \&isIconRes,     ],
320  );
321
322# - param numbers is [1..n] or [-1..-n] for optional params
323# - isRessource gets the unquoted potential ressource and
324#   returns the plain ressource name or undef (if it's definitely no ressource)
325
326my $defs                = scalar(@defs);
327my $errors              = 0;
328my $LOC                 = 0;
329my $showSpecialWarnings = 0;
330
331sub scanCodeFile($) {
332  my ($file) = @_;
333  open(FILE,'<'.$file) || die "can't read '$file' (Reason: $!)";
334
335  my @file = <FILE>;
336  my $flines = scalar(@file);
337  unshift @file, undef; # line 0
338
339  my $lineNr = 0;
340  eval {
341    for ($lineNr=1; $lineNr<=$flines; $lineNr++) {
342      my $line = $file[$lineNr];
343      # if ($line =~ /kernlin/io) { print "$file:$lineNr: Custom-Search: ".trim($line)."\n"; } # used to test this script
344
345      for (my $d=0; $d<$defs; $d++) {
346        my $def_r = $defs[$d];
347        my $reg = $$def_r[0];
348        if ($line =~ $reg) {
349          my $rest = $';
350          my $match = $&;
351          # print "reg='$reg'\n";
352          # print "$file:$lineNr: Match def #$d: $line";
353
354          chomp($rest);
355          my $calltype;
356          my @params = scanParams($rest,@file,$lineNr+1,$calltype);
357
358          if ($calltype==2) {
359            my $pnum_r = $$def_r[1]; 
360            my $pis = scalar(@$pnum_r);
361            for (my $pi=0; $pi<$pis; $pi++) { # for all params referencing a ressource
362              my $pnum = $$pnum_r[$pi];
363              my $param = $params[$pnum<0 ? -$pnum-1 : $pnum-1];
364
365              if (defined $param) {
366                $param = trim($param);
367                my $unquoted = isQuoted($param);
368                if (defined $unquoted) {
369                  my $test_r = $$def_r[3];
370                  my @unquoted = &$test_r($unquoted); # test if definitely NO ressource
371
372                  if (scalar(@unquoted)>0) { # possible ressource(s)
373                    my $idx_r = $$def_r[2]; # ressource index
374                    my $used = 0;
375                  UNQUOTED: foreach my $unquoted (@unquoted) {
376                      my $full_ressource_idx = $$idx_r{$unquoted};
377                      if (defined $full_ressource_idx) { # existing ressource
378                        my $full_ressource = $rel2full{$unquoted};
379                        if (not defined $full_ressource) { die "expected ressource '$unquoted' to be defined"; }
380                        $used{$full_ressource} = 1;
381                        $used = 1;
382                        last UNQUOTED;
383                      }
384                    }
385
386                    if ($used==0) {
387                      print "$file:$lineNr: Error: Ressource '".$unquoted[0]."' is missing\n";
388                    }
389                  }
390                }
391                else {
392                  if ($showSpecialWarnings==1) {
393                    print "$file:$lineNr: Warning: Param '$param' is not an explicit ressource, can't check\n";
394                    # print "Params:\n"; foreach (@params) { print "- param='$_'\n"; }
395                  }
396                }
397              }
398              else {
399                if ($pnum>0) {
400                  print "$file:$lineNr: Warning: Param #$pnum is missing, can't check\n";
401                }
402              }
403            }
404          }
405          else {
406            if ($showSpecialWarnings==1 and $calltype!=3) { # don't warn about function definition
407              print "$file:$lineNr: Warning: Matched '$match', but wrong calltype (=$calltype)\n";
408            }
409          }
410        }
411      }
412    }
413  };
414  if ($@) {
415    print "$file:$lineNr: Error: $@\n";
416    $errors++;
417    # if ($@ =~ /enough/) { die "enough"; }
418  }
419  close(FILE);
420  $LOC += $flines;
421}
422
423sub scanCodeFile_forUnuseds($\$\%) {
424  my ($file,$reg_r,$seen_r) = @_;
425  open(FILE,'<'.$file) || die "can't read '$file' (Reason: $!)";
426
427  my @file = <FILE>;
428  my $flines = scalar(@file);
429  unshift @file, undef;         # line 0
430
431  my $lineNr = 0;
432  for ($lineNr=1; $lineNr<=$flines; $lineNr++) {
433    my $line = $file[$lineNr];
434    while ($line =~ $$reg_r) {
435      my $res = $&;
436      # print "$file:$lineNr: Warning: Checker failed to detect potential usage of ressource '$res'\n";
437      my $seen = $$seen_r{$res};
438      if (defined $seen) { $seen .= ",$file:$lineNr"; }
439      else { $seen = "$file:$lineNr"; }
440      $$seen_r{$res} = $seen;
441      $line = $';
442    }
443  }
444  close(FILE);
445}
446
447sub scanCode() {
448  my @sources = ();
449  {
450    my %oldKnown = %known;
451    scanFiles(@sources, $ARBHOME, '\.[ch](xx|pp){0,1}$', 1, 1); # destroys %known and %unknown
452    print 'Checking '.scalar(@sources)." source files.\n";
453
454    %known = %oldKnown;
455    %unknown = ();
456  }
457
458  @sources = sort @sources;
459  foreach (@sources) { scanCodeFile($_); }
460
461  print "Scanned $LOC LOC.\n";
462
463  my %unused = ();
464  foreach (sort keys %known) {
465    if (not defined $used{$_}) { $unused{$_} = 1; }
466  }
467
468  my $unused = scalar(keys %unused);
469
470  if ($unused>0) {
471    print "Detected $unused unused ressources.\nRunning brute force scan..\n";
472    my $reg_unused = '';
473    foreach (keys %unused) { $reg_unused .= '|'.quotemeta($full2rel{$_}); }
474    $reg_unused = substr($reg_unused,1);
475    print "reg_unused='$reg_unused'\n";
476    $reg_unused = qr/$reg_unused/;
477
478    my %seen_unused = ();
479    foreach (@sources) {
480      scanCodeFile_forUnuseds($_,$reg_unused, %seen_unused);
481    }
482
483    foreach (sort keys %unused) {
484      my $rel = $full2rel{$_};
485      my $seen = $seen_unused{$rel};
486      if (defined $seen) {
487        print "$_:0: Warning: Checker failed to detect ressource usage\n";
488        my @seen = split(',',$seen);
489        my %seen = map { $_ => 1; } @seen;
490        foreach (sort keys %seen) {
491          print "$_: Warning: '$rel' possibly used here\n";
492        }
493      }
494      else {
495        print "$_:0: Error: Ressource is most likely unused\n";
496      }
497    }
498  }
499}
500
501sub main() {
502  print "Checking ARB ressources\n";
503
504  scanExistingRessources();
505  print ' - '.scalar(@pictures)." pictures\n";
506  print ' - '.scalar(@pixmaps)." images\n";
507  print ' - '.scalar(@helpfiles)." helpfiles\n";
508
509  scanCode();
510  if ($errors>0) { die "$errors errors detected by ressource checker\n"; }
511}
512
513main();
Note: See TracBrowser for help on using the repository browser.