-#! /bin/sh
-
-fis="${*:-*.o}"
-
-for i in $fis;do
- rest="`echo '' $fis ''|sed "s# $i # #"`"
- trail="()"
- for t in T BCDRS;do
- for sym in `nm $i|sed -n 's/^.* ['$t'] \(.*\)$/\1/p'`;do
- if nm $rest|grep -q ' U '"$sym"'$';then :;else
- echo "$i $sym$trail"|sed 's/\.o /.c /'
- fi
- done
- trail=""
- done
-done
-
-echo
-
-function checkextern
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use Cwd;
+use Data::Dumper;
+use Getopt::Long;
+
+
+my(%symtab)=();
+
+my $D;
+my $opt_nm="nm";
+my $opt_headers=1;
+die if !GetOptions(
+ "d|debug+",\$D,
+ "nm=s" ,\$opt_nm,
+ "headers!",\$opt_headers,
+ );
+
+
+sub defmiss
+{
+my($ref,$missval,$sub)=@_;
+
+ $$ref=$missval if !defined $$ref;
+ &{$sub}($ref);
+}
+
+sub addsymtype
+{
+my($slot,$type,$symref)=@_;
+
+ defmiss(\$slot,{},sub {
+ my($typeref)=@_;
+ push(@{$$$typeref{$type}},$symref);
+ });
+}
+
+sub storesym
{
- ctags -f - --c-types=$1 `cvsfiles|grep '\.h$'` | \
- if $2;then
- grep ' /\^extern '
- else
- cat
- fi \
- | while read -r line;do
- sym="` echo "$line" | awk '{print $1}'`"
- file="`echo "$line" | awk '{print $2}'`"
- if nm $fis|grep -q " [$3] ""$sym"'$';then :;else
- echo "extern $file $sym$4"
- fi
- done
+my(%sym)=@_;
+
+ $sym{"fn_c"}=~s#^$ENV{PWD}/#./# if defined $sym{"fn_c"};
+ defmiss(\$symtab{$sym{"name"}},{},sub {
+ my($slotref)=@_;
+ my($type)=$sym{"type"};
+ addsymtype($$slotref,$type,\%sym);
+ addsymtype($$slotref,$type,\%sym)
+ if ($type!~/nU/ && ($type=~s/(n)[[:upper:]]/$1+/ || $type=~s/(n)[[:lower:]]/$1-/));
+ });
}
-checkextern x true BCDRS ""
-checkextern p false T "()"
+open(NM,"-|",'find -name "*.o"|xargs -- '.$opt_nm.' -f posix --print-file-name --no-sort --line-numbers')
+ or die "Cannot get nm output: $!";
+while (<NM>) {
+ chomp;
+ die "Line $. of nm output not recognized: $_"
+ # $1 $2 $3 $4
+ if !/^([^:]*):\s*(\S*) (\w) [^\t]*(?:\t(.*))?$/;
+ storesym(
+ "fn_o"=>$1,
+ "name"=>$2,
+ "type"=>"n$3",
+ "fn_c"=>$4,
+ );
+ }
+close NM;
+
+if ($opt_headers) {
+ open(CTAGS,"-|",'find -name "*.h"|xargs ctags -f - --c-types=xp')
+ or die "Cannot get ctags output: $!";
+ while (<CTAGS>) {
+ chomp;
+ die "Line $. of ctags output not recognized: $_"
+ # $1 $2 $3
+ if !/^([^\t]*)\t([^\t]*)\t.*\t(\w)$/;
+ storesym(
+ "name"=>$1,
+ "fn_c"=>$2,
+ "type"=>"c$3",
+ );
+ }
+ close CTAGS;
+ }
+
+print Data::Dumper->Dump([\%symtab],["%symtab"]) if $D;
+
+sub dumpsyms
+{
+my($error,@arr)=@_;
+
+ print("$error ".$arr[0]{"name"}.($arr[0]{"type"}=~/cp|n[tT]/ ? "()" : "").":\n");
+ my($symref);
+ while ($symref=shift(@arr)) {
+ print("\t");
+ if (defined($$symref{"fn_c"})) {
+ print($$symref{"fn_c"});
+ print("\t(".$$symref{"fn_o"}.")") if defined $$symref{"fn_o"};
+ }
+ else {
+ my($fn)=$$symref{"fn_o"};
+ $fn=~s/\.o$/.c/;
+ print($fn);
+ }
+ print("\n");
+ }
+}
+
+sub refendef
+{
+my($ref)=@_;
+
+ return [] if !defined($ref);
+ return $ref;
+}
+
+my($symname);
+foreach $symname (sort keys %symtab) {
+ my($typesref)=$symtab{$symname};
+ my($ref,$ref1,$ref2);
+
+# >=2 n[:upper:]\U symbols => global conflict
+ if (@{refendef($ref=$$typesref{"n+"})}>=2) {
+ dumpsyms("global conflict",@{$ref});
+ }
+
+# >=1 n[:upper:]\U symbol && >=1 n[:lower:] symbol => local override
+ if (@{refendef($ref1=$$typesref{"n+"})}>=1 && @{refendef($ref2=$$typesref{"n-"})}>=1) {
+ dumpsyms("local override of global",@{$ref1});
+ dumpsyms(" -- with local",@{$ref2});
+ }
+
+# >=1 n[:upper:]\U symbol && ==0 nU symbol => global singularity
+ if (@{refendef($ref=$$typesref{"n+"})}>=1 && @{refendef($$typesref{"nU"})}==0) {
+ dumpsyms("global singularity",@{$ref});
+ }
+
+# >=1 cx symbol && ==0 n[:upper:]\U symbol => floating extern data
+ if (@{refendef($ref=$$typesref{"cx"})}>=1 && @{refendef($$typesref{"n+"})}==0) {
+ dumpsyms("floating extern data",@{$ref});
+ }
+
+# >=1 cp symbol && ==0 n[:upper:]\U symbol => floating extern func
+ if (@{refendef($ref=$$typesref{"cp"})}>=1 && @{refendef($$typesref{"n+"})}==0) {
+ dumpsyms("floating extern func",@{$ref});
+ }
+ }