#!/usr/local/bin/perl -w sub Exclude { my $cfile = shift; if (open(C,"<$cfile")) { while () { if (/{\s*"[^"]+"\s*,\s*(\w+)\s*}/) { $Exclude{$1} = $cfile; } } close(C); } else { warn "Cannot open $cfile:$!"; } } sub Vfunc { my $hfile = shift; my %VFunc = (); my %VVar = (); my %VError= (); open(H,"<$hfile") || die "Cannot open $hfile:$!"; while () { if (/^\s*(EXTERN|extern)\s*(.*?)\s*(\w+)\s+_\s*\(\(/) { my ($type,$name) = ($2,$3); my $defn = "VFUNC($type,$name,V_$name,_(("; $_ = $'; until (/\)\);\s*$/) { $defn .= $_; $_ = ; if (/^\S/) { chomp($_); die $_; } } s/\)\);\s*$/)))\n/; $defn .= $_; $VFunc{$name} = $defn; } elsif (/^\s*(EXTERN|extern)\s*(.*?)\s*(\w+)\s*;/) { my ($type,$name) = ($2,$3); $VVar{$name} = "VVAR($type,$name,V_$name)\n"; } elsif (/\b(EXTERN|extern)\b/) { die "$hfile:$.: $_" unless (/^\s*#\s*define/); } } close(H); if (keys %VFunc || keys %VVar) { my $gard = "\U$hfile"; $gard =~ s/\..*$//; $gard =~ s#/#_#g; my $name = "\u\L${gard}\UV"; $fdef = $hfile; $fdef =~ s/\..*$/.t/; $mdef = $hfile; $mdef =~ s/\..*$/.m/; my $htfile = $hfile; $htfile =~ s/\..*$/_f.h/; unless (-r $htfile) { open(C,">$htfile") || die "Cannot open $htfile:$!"; print C "#ifndef ${gard}_VT\n"; print C "#define ${gard}_VT\n"; print C "#include \"$hfile\"\n"; print C "typedef struct ${name}tab\n{\n"; print C "#define VFUNC(type,name,mem,args) type (*mem) args;\n"; print C "#define VVAR(type,name,mem) type (*mem);\n"; print C "#include \"$fdef\"\n"; print C "#undef VFUNC\n"; print C "#undef VVAR\n"; print C "} ${name}tab;\n"; print C "extern ${name}tab *${name}ptr;\n"; print C "extern ${name}tab *${name}Get _((void));\n"; print C "#endif /* ${gard}_VT */\n"; close(C); } my $cfile = $hfile; $cfile =~ s/\..*$/_f.c/; unless (-r $cfile) { open(C,">$cfile") || die "Cannot open $cfile:$!"; print C "#include \"$hfile\"\n"; print C "#include \"$htfile\"\n"; print C "static ${name}tab ${name}table =\n{\n"; print C "#define VFUNC(type,name,mem,args) name,\n"; print C "#define VVAR(type,name,mem) &name,\n"; print C "#include \"$fdef\"\n"; print C "#undef VFUNC\n"; print C "#undef VVAR\n"; print C "};\n"; print C "${name}tab *${name}ptr;\n"; print C "${name}tab *${name}Get() { return ${name}ptr = &${name}table;}\n"; close(C); } print STDERR "$gard\n"; chmod(0666,$fdef) unless -w $fdef; open(VFUNC,">$fdef") || die "Cannot open $fdef:$!"; chmod(0666,$mdef) unless -w $mdef; open(VMACRO,">$mdef") || die "Cannot open $mdef:$!"; print VFUNC "#ifdef _$gard\n"; print VMACRO "#ifndef _${gard}_VM\n"; print VMACRO "#define _${gard}_VM\n"; print VMACRO "#include \"$htfile\"\n"; foreach $func (sort keys %VVar) { if (!exists $Exclude{$func}) { print VFUNC $VVar{$func}; print VMACRO "#define $func (*${name}ptr->V_$func)\n" } } foreach $func (sort keys %VFunc) { if (!exists $Exclude{$func}) { print VFUNC $VFunc{$func}; print VMACRO "#define $func (*${name}ptr->V_$func)\n" } } print VMACRO "#endif /* _${gard}_VM */\n"; close(VMACRO); print VFUNC "#endif /* _$gard */\n"; close(VFUNC); # Close this last - Makefile dependancy } } foreach () { Exclude($_); } foreach (@ARGV) { Vfunc($_); } __END__ =head1 NAME mkVFunc - Support for "nested" dynamic loading =head1 SYNOPSIS mkVFunc xxx.h =head1 DESCRIPTION B is designed so that B can be dynamically loaded 'on top of' perl. That is the easy bit. What it also does is allow Tk::Xxxx to be dynamically loaded 'on top of' the B composite. Thus when you 'require Tk::HList' the shared object F<.../HList.so> needs to be able to call functions defined in perl I functions defined in loadable .../Tk.so . Now functions in 'base executable' are a well known problem, and are solved by DynaLoader. However most of dynamic loading schemes cannot handle one loadable calling another loadable. Thus what Tk does is build a table of functions that should be callable. This table is auto-generated from the .h file by looking for 'extern' (and EXTERN which is #defined to 'extern'). Thus any function marked as 'extern' is 'referenced' by the table. The address of the table is then stored in a perl variable when Tk is loaded. When HList is loaded it looks in the perl variable (via functions in perl - the 'base executable') to get the address of the table. The same utility that builds the table also builds a set of #define's. HList.c (and any other .c files which comprise HList) #include these #define's. So that Tk_SomeFunc(x,y,z) Is actually compiled as (*TkVptr->V_Tk_SomeFunc)(x,y,z) Where Tk_ptr is pointer to the table. See: Tk-b*/pTk/mkVFunc - perl script that produces tables /tk.h - basis from which table is generated /tk.m - #define's to include in sub-extension /tk_f.h - #included both sides. /tk_f.c - Actual table definition. /tk.t - 'shared' set of macros which produce table included in tk_f.c and tk_f.h /tkVMacro.h - Wrapper to include *.m files In addition to /tk* there are /tkInt*, /Lang* and /tix* =cut