Create "williamr" directory in utilities, and add an assortment of Perl scripts
authorWilliam Roberts <williamr@symbian.org>
Mon, 01 Jun 2009 15:26:59 +0100
changeset 2 a600c1a596f7
parent 1 4a4ca5a019bb
child 3 8b87ea768cb8
Create "williamr" directory in utilities, and add an assortment of Perl scripts
williamr/Read_me.txt
williamr/compare_7z_listings.pl
williamr/find_public_apis.pl
williamr/sbs_findstr.pl
williamr/scan_antlogs.pl
williamr/scan_epocwind.pl
williamr/scan_sbs_makefile.pl
williamr/sfl_exports.pl
williamr/sort_uniq.pl
williamr/summarise_rnd_binaries.pl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/Read_me.txt	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,3 @@
+What's in this directory
+
+Various Perl scripts, generally about list processing and analysis
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/compare_7z_listings.pl	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Compare two 7z listings, looking for files present in both
+
+use strict;
+
+my %first_files;
+my $line;
+my $file_index = -1;
+
+while ($line=<>)
+  {
+  
+  if ($line =~ /^Listing archive/)
+    {
+    $file_index++;
+    print "$file_index: Processing $line";
+    next;
+    }
+
+  # 2009-04-30 11:26:58 D....            0            0  epoc32\cshlpcmp_template
+  # 2009-03-20 22:22:18 .....        72192        16307  epoc32\cshlpcmp_template\cshelp2000.dot
+  
+  next if (length($line) < 54);
+  
+  my $dir_attribute = substr($line, 20, 1);  
+  if ($dir_attribute eq ".")
+    {
+    chomp $line;
+    my $fullpath = substr($line, 53);
+    
+    if ($file_index == 0)
+      {
+      # first file
+      $first_files{$fullpath} = $line;
+      next;
+      }
+    if (defined $first_files{$fullpath})
+      {
+      print "Duplicate filename: $fullpath\n";
+      print "\t$first_files{$fullpath}\n\t$line\n";
+      next
+      }
+    }
+  }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/find_public_apis.pl	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,88 @@
+#!/usr/bin/perl
+
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Identify "Public APIs" - defined as
+# 1. Files in epoc32\include which are not in epoc32\include\platform, 
+# 2. And contain either no Symbian API classification doxygen tags (Public by export)
+# 3. Or contain @publishedAll (Public by tag - now deprecated)
+
+use strict;
+my $debug = 0;
+
+sub is_public_api($$)
+  {
+  my ($file,$name) = @_;
+  
+  if ($name =~ /^epoc32\/include\/platform\//)
+    {
+    # /epoc32/include/platform files are "Platform by export"
+    return 0; # Not public
+    }
+  
+  open FILE, "<$file" or print "ERROR: Cannot open $file: $!\n" and return 1; # assume Public
+  my @lines = <FILE>; # they are all of a modest size
+  close FILE;
+  
+  my @apitaglines = grep /\@published|\@internal/, @lines;
+  if (scalar @apitaglines == 0)
+    {
+    # no API classification tags - must be "Public by export" 
+    return 1; # Public API
+    }
+  
+  if ($debug)
+    {
+    print join("\n\t", $file, @apitaglines), "\n";
+    }
+  my @publishedAll = grep /\@publishedAll/, @apitaglines;
+  if (scalar @publishedAll == 0)
+    {
+    # the API classification tags are all @publishedPartner or @internal
+    return 0; # not public
+    }
+  # contains at least one @publishedAll element - must be "Public by tag"
+  return 1; # Public API
+  }
+
+sub scan_directory($$)
+  {
+  my ($path, $name) = @_;
+  
+  opendir DIR, $path;
+  my @files = grep !/^\.\.?$/, readdir DIR;
+  closedir DIR;
+  
+  foreach my $file (@files)
+    {
+    my $newpath = "$path/$file";
+    my $newname = "$name/$file";
+    
+    if (-d $newpath)
+      {
+      scan_directory($newpath, $newname);
+      next;
+      }
+    
+    if (is_public_api($newpath,$newname))
+      {
+      print "$newname\n";
+      }
+    else
+      {
+      # print "PARTNER\t$newname\n";
+      }
+    }
+  }
+
+scan_directory("/epoc32/include", "epoc32/include");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/sbs_findstr.pl	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,43 @@
+#! perl
+
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Filter an SBSv2 log to keep only recipes which match a specified RE
+
+use strict;
+
+my $expression = shift @ARGV;
+my $line;
+my $skipping = 1;
+
+while ($line =<>)
+  {
+  if (substr($line,0,9) eq "</recipe>")
+    {
+    print $line if ($skipping == 0);  
+    $skipping = 1;    # set this to 0 to get the "between recipes" stuff
+    next;
+    }
+  if (substr($line,0,8) eq "<recipe ")
+    {
+    if ($line =~ /$expression/io)
+      {
+      $skipping = 0;
+      }
+    else
+      {
+      $skipping = 1;
+      }
+    }
+  print $line if ($skipping == 0);  
+  }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/scan_antlogs.pl	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,216 @@
+#!/usr/bin/perl
+
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Parse "ant" logs from SBS build to determine missing source files
+
+my $pdk_src = "../.."; # path to sf tree - correct from "build/output"
+
+my %missing_files;
+my %damaged_components;
+my %excluded_things;
+my %abld_import;
+my %damaged_bldinfs;
+
+sub canonical_path($)
+  {
+  my ($path) = @_;
+  my @bits = split /\//, $path;
+  my @newbits = ();
+  
+  foreach my $bit (@bits)
+    {
+    next if ($bit eq ".");
+    if ($bit eq "..")
+      {
+      pop @newbits;
+      next;
+      }
+      push @newbits, $bit;
+    }
+  return join("/", @newbits);
+  }
+
+sub excluded_thing($$$)
+  {
+  my ($path, $missing, $reason) = @_;
+  if (!defined $excluded_things{$path})
+    {
+    @{$excluded_things{$path}} = ();
+    }
+  push @{$excluded_things{$path}}, $missing;
+  # print "Missing $missing from excluded $path ($reason)\n";
+  }
+
+sub do_missing_file($$$)
+  {
+  my ($missing, $missing_from, $reason) = @_;
+  
+  $missing = canonical_path($missing);
+  $missing_from = canonical_path($missing_from);
+  
+  my $component = "??";
+  if ($missing_from ne "??")
+    {
+    my @dirs = split /\//, $missing_from;
+    shift @dirs if ($dirs[0] eq "sf");
+    
+    $path = $pdk_src . "/sf/$dirs[0]/$dirs[1]";
+    if (!-e $path)
+      {
+      # no sign of the package
+      excluded_thing($path, $missing, $reason);
+      return;
+      }
+    $path .= "/$dirs[2]";
+    if (!-e $path)
+      {
+      # no sign of the collection
+      excluded_thing($path, $missing, $reason);
+      return;
+      }
+    $path .= "/$dirs[3]";
+    if (!-e $path)
+      {
+      # no sign of the component
+      excluded_thing($path, $missing, $reason);
+      return;
+      }
+    $component = join("/", $dirs[0], $dirs[1], $dirs[2], $dirs[3]);
+    }
+  
+  $missing_files{$missing} = $reason if ($missing ne "??");
+  
+  if (!defined $damaged_components{$component})
+    {
+    @{$damaged_components{$component}} = ();
+    }
+  push @{$damaged_components{$component}}, $missing;
+  }
+
+sub scan_logfile($)
+{
+  my ($logfile) = @_;
+  
+  open FILE, "<$logfile" or print "Error: cannot open $logfile: $!\n" and return;
+  
+  my $line;
+  while ($line = <FILE>)
+    {
+    # Could not export s:/sf/mw/classicui/commonuisupport/uikon/docs/Uikon_1.2_Caps_Lock_Extension.doc to s:/epoc32/engdoc/application_framework/uikon/uikon_1.2_caps_lock_extension.doc
+    if ($line =~ /^Could not export .*\/(sf\/.*) to .:\/(epoc32\/.*)$/)
+      {
+      my $source = $1;
+      my $exported = $2;
+      if (-e "m:/$exported")
+        {
+        $abld_import{$source} = $exported;
+        }
+      next;
+      }
+    # Source of export does not exist:  s:/sf/mw/messagingmw/messagingfw/msgtests/group/msgerr.ra
+    # Source zip for export does not exist: s:/sf/os/deviceplatformrelease/S60LocFiles/data/96.zip
+    if ($line =~ /^Source (of|zip for) export does not exist.\s+.*\/(sf\/.*)$/)
+      {
+      do_missing_file($2, "??", "source of export");
+      next;
+      }
+    # No bld.inf found at sf/os/buildtools/toolsandutils/burtestserver/Group in s:/output/build/canonical_system_definition_GT_tb91sf.xml
+    # No bld.inf found at s:/sf/adaptation/stubs/licensee_tsy_stub/group in s:/output/build/canonical_system_definition_S60_5_1_clean.xml
+    if ($line =~ /No bld.inf found at (.*\/)?(sf\/.*) in /i)
+      {
+      my $bldinf = "$2/bld.inf";
+  
+      do_missing_file($bldinf, $bldinf, "no bld.inf");
+      $damaged_bldinfs{"$bldinf\t(missing)"} = 1;
+      next;
+      }
+    # D:/Symbian/Tools/PDT_1.0/raptor/win32/mingw/bin/cpp.exe: s:/sf/os/networkingsrv/networksecurity/ipsec/group/bld.inf:19:42: ../eventmediator/group/bld.inf: No such file or directory
+    if ($line =~ /cpp.exe: .*\/(sf\/[^:]*):.*\s+([^:]+): No such file/)
+      {
+      my $parent = $1;
+      my $relative = $2;
+  
+      if ($parent =~ /\.inf$/i)
+        {
+        my $parent = canonical_path($parent);
+        $damaged_bldinfs{"$parent\t$relative"} = 1;
+        }
+      do_missing_file("$parent/../$relative", $parent, "#include");
+      next;  
+      }
+    }
+    close FILE;
+  }
+  
+  my @logfiles = map(glob,@ARGV);
+  foreach my $logfile (@logfiles)
+    {
+    print "Scanning $logfile...\n";
+    scan_logfile($logfile);
+    }
+  
+  printf "%d Excluded things\n", scalar keys %excluded_things;
+  foreach my $component (sort keys %excluded_things)
+    {
+    my @list = @{$excluded_things{$component}};
+    my %hash;
+    foreach my $missing (@list)
+      {
+      $hash{$missing} = 1;
+      }
+    printf "%s\t%d\n", $component, scalar keys %hash;
+    print "\t", join("\n\t", sort keys %hash), "\n";
+    }
+  print "\nDamaged components\n";
+  foreach my $component (sort keys %damaged_components)
+    {
+    my @list = @{$damaged_components{$component}};
+    my %hash;
+    foreach my $missing (@list)
+      {
+      $hash{$missing} = 1;
+      }
+    printf "%s\t%d\n", $component, scalar keys %hash;
+    print "\t", join("\n\t", sort keys %hash), "\n";
+    }
+  print "\nMissing files\n";
+  foreach my $missing (sort keys %missing_files)
+    {
+    my $exported = $abld_import{$missing};
+    $exported = "(not in PDK)" if (!defined $exported);
+    my $reason = $missing_files{$missing};
+    my @dirs = split /\//, $missing;
+    my $path = shift @dirs;
+    my $dir;
+    
+    while ($dir = shift @dirs)
+      {
+      if (-e "$pdk_src/$path/$dir")
+        {
+        # still exists at this point
+        $path .= "/$dir";
+        next;
+        }
+      print "\t$reason\t$path\t\t", join("/", $dir,@dirs), "\t$exported\n";
+      last;
+      }    
+    }
+  
+  print "\nDamaged bld.infs\n";
+  print join("\n", sort keys %damaged_bldinfs, "");
+  
+  print "\n\n";
+  printf "%d files missing from ", scalar keys %missing_files;
+  printf "%d damaged components\n", scalar keys %damaged_components;
+ 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/scan_epocwind.pl	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,65 @@
+#! perl
+
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Summarise epocwind.out to identify repetitious and uninteresting comments
+
+use strict;
+
+my %count_by_word;
+my %unique_message;
+
+my $line;
+while ($line = <>)
+  {
+  chomp $line;
+  
+  #    494.390	CTouchFeedbackImpl::SetFeedbackArea - Begin
+  my $message = substr($line, 10);  # ignore the timestamp & the tab character
+  if (!defined $unique_message{$message})
+    {
+    $unique_message{$message} = 0;
+    }
+  $unique_message{$message} ++;
+  
+  my ($junk,$count,$word) = split /\s+|:/, $line;
+  $word = $message if (!defined $word);   # no spaces in the line at all
+
+  if (!defined $count_by_word{$word})
+    {
+    $count_by_word{$word} = 0;
+    }
+  $count_by_word{$word} ++;
+  
+  }
+
+my @repeated_lines;
+foreach my $message (keys %unique_message)
+  {
+  my $count = $unique_message{$message};
+  next if ($count < 10);
+  push @repeated_lines, sprintf "%7d\t%s\n", $count, $message;
+  }
+
+print "Repeated lines\n", reverse sort @repeated_lines, "\n";
+
+my @repeated_words;
+foreach my $word (keys %count_by_word)
+  {
+  my $count = $count_by_word{$word};
+  next if ($count < 10);
+  push @repeated_words, sprintf "%7d\t%s\n", $count, $word;
+  }
+
+print "Repeated words (rest of the line may vary\n", reverse sort @repeated_words, "\n";
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/scan_sbs_makefile.pl	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,121 @@
+#!/usr/bin/perl
+
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Approximate "abld -what" from SBS Makefile.default and Makefile.export
+
+use strict;
+my $component = "";
+my $mmp = "";
+my $linkpath = "";
+my $target = "";
+my $targettype = "";
+my $exports = "";
+
+sub completed
+  {
+  if ($component ne "")
+    {
+    if ($exports eq "")
+      {
+      # Compilation makefile target
+      print "$component\t$mmp\t$linkpath/$target.$targettype\n";
+      }
+    else
+      {
+      # export makefile
+      my @exportpairs = split / /, $exports;
+      foreach my $pair (@exportpairs)
+        {
+        my ($dest,$src) = split /<-/, $pair;
+        $dest =~ s/^.:\///;
+        print "$component\texport\t$dest\n";
+        }
+      }
+    }
+  $component = "";
+  $mmp = "";
+  $linkpath = "";
+  $target = "";
+  $targettype = "";
+  $exports = "";
+  }
+
+sub scan_logfile($)
+  {
+  my ($logfile) = @_;
+  
+  open FILE, "<$logfile" or print "Error: cannot open $logfile: $!\n" and return;
+  
+  my $line;
+  while ($line = <FILE>)
+    {
+    # COMPONENT_META:=s:/sf/os/boardsupport/emulator/emulatorbsp/bld.inf
+    # PROJECT_META:=s:/sf/os/boardsupport/emulator/emulatorbsp/cakdwins.mmp
+    # LINKPATH:=winscw/udeb
+    # TARGET:=ekdata
+    # TARGETTYPE:=dll
+    # EXPORT:=s:/epoc32/tools/scanlog.pl<-s:/sf/os/buildtools/bldsystemtools/buildsystemtools/scanlog/scanlog.pl more...
+    # MAKEFILE_LIST:=
+    
+    if ($line =~ /^(COMPONENT_META|PROJECT_META|LINKPATH|TARGET|REQUESTEDTARGETEXT|EXPORT|MAKEFILE_LIST):=(.*)$/o)
+      {
+      my $variable = $1;
+      my $value = $2;
+      
+      if ($variable eq "MAKEFILE_LIST")
+        {
+        completed();
+        next;
+        }
+      if ($variable eq "COMPONENT_META")
+        {
+        $component = $value;
+        next;
+        }
+      if ($variable eq "PROJECT_META")
+        {
+        $mmp = $value;
+        next;
+        }
+      if ($variable eq "LINKPATH")
+        {
+        $linkpath = $value;
+        next;
+        }
+      if ($variable eq "TARGET")
+        {
+        $target = $value;
+        next;
+        }
+      if ($variable eq "REQUESTEDTARGETEXT")
+        {
+        $targettype = $value;
+        next;
+        }
+      if ($variable eq "EXPORT")
+        {
+        $exports = $value;
+        next;
+        }
+      }
+    }
+    close FILE;
+  }
+
+  my @logfiles = map(glob,@ARGV);
+  foreach my $logfile (@logfiles)
+    {
+    # print "Scanning $logfile...\n";
+    scan_logfile($logfile);
+    }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/sfl_exports.pl	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Match list of "sfl" files in epoc32 tree with whatlog information
+
+use strict;
+
+my %sfl_files;
+my $line;
+
+while ($line = <>)
+  {
+  chomp $line;
+  
+  # output of findstr /m /s /C:"www.symbianfoundation.org/legal/sfl-v" epoc32\*
+  # epoc32\data\z\private\101f7989\backup_registration.xml
+  if ($line =~ /^epoc32/)
+    {
+    $line =~ s/\\/\//g;   # Unix directory separators please
+    $sfl_files{$line} = "unknown";
+    next;
+    }
+  
+  # ..\/platform_MCL.PDK-3.8__winscw.whatlog_armv5.whatlog_multiple_threadWHAT_GT_tb91sf_compile.log(6824),
+  # sf/os/boardsupport/emulator/emulatorbsp/bld.inf,
+  # sf/os/boardsupport/emulator/emulatorbsp/specific/winscomm.h,
+  # export,
+  # epoc32/include/wins/winscomm.h,
+  # h
+  my ($log, $bldinf, $srcfile, $type, $epocfile, $extn) = split /,/, $line;
+
+  if (defined $sfl_files{$epocfile})
+    {
+    if ($type eq "export")
+      {
+      # direct export - should be easy to fix
+      $sfl_files{$epocfile} = $srcfile;
+      next;
+      }
+    $sfl_files{$epocfile} = "generated - $type";
+    next;
+    }
+  }
+
+foreach my $epocfile (sort keys %sfl_files)
+  {
+  print "$epocfile\t$sfl_files{$epocfile}\n";
+  }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/sort_uniq.pl	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,27 @@
+#! perl
+
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Make up for lack of "sort | uniq" on Windows
+
+use strict;
+my $line;
+
+my %uniq;
+while ($line = <>)
+  {
+  $uniq{$line} = 1;
+  }
+
+print sort keys %uniq;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/williamr/summarise_rnd_binaries.pl	Mon Jun 01 15:26:59 2009 +0100
@@ -0,0 +1,106 @@
+#! perl
+
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Perl script to summarise the R&D binaries listing
+
+use strict;
+
+my %grouped_by_basename;
+my $line;
+
+while ($line=<>)
+  {
+  # 2009-04-30 11:26:58 D....            0            0  epoc32\cshlpcmp_template
+  # 2009-03-20 22:22:18 .....        72192        16307  epoc32\cshlpcmp_template\cshelp2000.dot
+  
+  next if (length($line) < 54);
+  
+  my $dir_attribute = substr($line, 20, 1);  
+  if ($dir_attribute eq ".")
+    {
+    chomp $line;
+    my $fullpath = substr($line, 53);
+    my $filename = substr($fullpath, rindex($fullpath,"\\")+1);
+    my $basename = lc substr($filename, 0, index($filename,"."));
+    
+    if ($basename =~ /^(.*){[0-9a-f]+}$/)
+      {
+      # import library
+      $basename = $1;
+      }
+    elsif ($basename =~ /^(.*)_\d+$/)
+      {
+      # language variant in basename rather than extension
+      $basename = $1;
+      }
+    elsif ($basename =~ /^(.*)_(aif|reg)$/)
+      {
+      # Uikon file grouping
+      $basename = $1;
+      }
+
+    if (!defined $grouped_by_basename{$basename})
+      {
+      $grouped_by_basename{$basename} = ();
+      }
+    push @{$grouped_by_basename{$basename}}, $fullpath;
+    next;
+    }
+  }
+
+sub summarise_extensions(@)
+  {
+  my @files = @_;
+  my $resources = 0;
+  my $exes = 0;
+  my $dlls = 0;
+  my $libs = 0;
+  my $maps = 0;
+  my $headers = 0;
+  my $others = 0;
+  my %what_others;
+  
+  foreach my $file (@files)
+    {
+    my $extension = substr($file,rindex($file, "."));
+
+    if ($extension =~ /^.r\d+$/io)
+      {
+      $what_others{".rNN"} += 1;
+      next;
+      }
+    if ($extension =~ /^.o\d+$/io)
+      {
+      $what_others{".oNNNN"} += 1;
+      next;
+      }
+    $what_others{$extension} += 1;
+    }
+  foreach my $extension (sort keys %what_others)
+    {
+    printf "%d %s, ", $what_others{$extension}, $extension;
+    }
+  print "\n";
+  }
+
+my $count = 0;
+foreach my $basename (sort keys %grouped_by_basename)
+  {
+  my @files = @{$grouped_by_basename{$basename}};
+  next if (! grep /winscw|tools/, @files);  # ignore ARMV5 only for now...
+  printf "%6d\t%s\t", scalar @files, $basename;
+  summarise_extensions(@files);
+  $count++;
+  }
+printf "%d distinct missing basenames (from %d total)\n", $count, scalar keys %grouped_by_basename;