WindowsXP-SP1/tools/sp/symmake.pm

706 lines
22 KiB
Perl

package SymMake;
use lib $ENV{RAZZLETOOLPATH} . "\\sp";
use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts";
use lib $ENV{RAZZLETOOLPATH};
use strict;
use Carp;
use IO::File;
use Data::Dumper;
use File::Basename;
use File::Find;
use Logmsg;
# Data structure
# pdbname.binext => [(var)pdbpath,size,$binext]
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
#
# Package SymMake
# - create symbols.inf, symbols(n).ddf and symbols.cdf
#
# in this version, the symmake.pm creates full package's and update package's symbols.inf, symbols.ddf and symbols.cdf
#
# for doing this, the symmake.pm first reads the symbol list from arch, then reads another one from symbolscd.txt;
# every reading, it store the record to $self->{'SYM'} and store the new file extension to $self->{'EXT'}
#
# then, we according $pkinfoptr to write relate information to symbols.inf, symbols(n).ddf and symbols.cdf
#
# [Data Relations]
# $pkinfoptr and $self->{'SYM'} are the two mainly hash we operating with
# $pkinfoptr - the package information includes the cab, ddf, inf's file names and the file handles
# $self->{'SYM'} - the symbols records that are from archive or ntpostbld
#
# basically, we looping for each record in $self->{'SYM'},
# according the $self->{'SYM'}->{"$symbol\.$installpath"}->[0] to get the kb term ('ARCH' or 'NTPB')
# then, we use %revpktypes to get $pktype ('FULL' or 'UPDATE')
#
# or, we according $pktype ('FULL' or 'UPDATE') to get kb term from $pktypes
# then, we can access the root of arch or ntpostbld ($self->{'KB'}->{$pktype})
#
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
#
# $DDFHandle - the .ddf file handle for writing symbols(n).ddf
# $CATHandle - the .cat file handle for writing content to .cdf for creating .cat(alog)
# $INFHandle - the .inf file handle for writing symbols.inf
#
my ($DDFHandle, $CATHandle, $INFHandle);
#
# Package Types
# FULL - from ARCH
# UPDATE - from NTPOSTBLD
#
my %pktypes = (
FULL => 'ARCH',
UPDATE => 'NTPB'
);
#
# %revpktypes = ( # reverse the key - value relation for %pktypes
# 'ARCH' => 'FULL'
# 'NTPB' => 'UPDATE'
# )
#
my %revpktypes = reverse %pktypes;
#
# $obj = new SymMake $archpath, $ntpostbldpath
# - object creator
#
# ( we assign the root and use the relation path in symbolcd.txt and archiver dump list )
# return:
# $obj->
# 'KB' -> # Knowledge Base
# 'ARCH' => \\arch\archive\....
# 'NTPB' => %_NTPOSTBLD%
# 'SYM' -> (empty) # The file lists of symbols, hash structure is listed in below:
# # $self->{'SYM'}->
# # $symbol\.$installpath -> # such as 'ntoskrnl.pdb.exe'
# # [kbterm, symbol subpath, symbol filesize, $installpath] # such as ['ARCH', "\\symbols\\retail\\exe\\ntoskrnl.pdb", 4189184, 'exe']
# 'EXT' -> (empty) # The file lists of symbols' file extension, hash structu is listed in below:
# # $self->{'EXT'}->
# # $pktype -> #such as 'FULL'
# # $installpath -> 1 # such as 'EXE' -> 1
# 'HANDLE' -> (empty) # The file handles
# 'PKTYPE' -> undef # package type
#
sub new {
my $class = shift;
my $instance = {
KB => {
"$pktypes{'FULL'}" => $_[0],
"$pktypes{'UPDATE'}" => $_[1]
},
SYM => {},
EXT => {},
HANDLE => {},
PKTYPE => undef
};
return bless $instance, $class;
}
#
# $obj->ReadSource($symbolcd)
# - read the source file ($symbolcd) to $self->{'SYM'} and $self->{'EXT'}
# IN: $symbolcd
# REF: $self->{'PKTYPE'} : current package type
#
#
#
sub ReadSource
{
my ($self, $symbolcd) = @_;
my ($fh, $kbterm, $mykey, @mylist);
local $_;
$kbterm = $pktypes{$self->{'PKTYPE'}};
$symbolcd = "$self->{'KB'}->{$kbterm}\\symbolcd\\symbolcd.txt" if (!defined $symbolcd);
$symbolcd = "$ENV{'TEMP'}\\symbolcd.txt" if (!-e $symbolcd);
if ($self->{'PKTYPE'} =~ /FULL/i) {
#
# for full package, we look for $symbolcd exist
# if exist, we load it
#
if (-e $symbolcd) {
($self->{'SYM'}, $self->{'EXT'}) = @{do $symbolcd}; # do command will load the script and evaluate (similar as 'require' command)
} else {
#
# if not exist, we create one in run time from archive path ($self->{'KB'}->{$kbterm})
#
$self->HashArchServer($self->{'KB'}->{$kbterm});
#
# for reuse it next time, we store it to $ENV{'TEMP'}\\symbolcd.txt
#
# so, if we want to create one and store to archive server, we can do below:
# [a.pl]
# use SymMake;
# ($the_archived_source_root, $my_dump_path_in_archive) = @ARGV;
# $symmake = new SymMake $the_archived_source_root, $ENV{_NTPOSTBLD};
# $symmake->ReadSource("$ENV{'TEMP'}\\symbolcd.txt");
# sytem("copy \"$ENV{'TEMP'}\\symbolcd.txt\" $my_dump_path_in_archive
# and call something similar as:
# perl a.pl "\\\\arch\\archive\\ms\\windows\\windows_xp\\rtm\\2600\\$BuildType\\all\\$BuildArch\\pub" "\\\\arch\\archive\\ms\\windows\\windows_xp\\rtm\\2600\\$BuildType\\all\\$BuildArch\\pub\\symbolcd\\symbolcd.txt"
#
$Data::Dumper::Indent=1;
$Data::Dumper::Terse=1;
$fh = new IO::File $symbolcd, 'w';
if (!defined $fh) {
logmsg "Cannot open $symbolcd\.";
} else {
print $fh 'return [';
print $fh Dumper($self->{'SYM'});
print $fh ",\n";
print $fh Dumper($self->{'EXT'});
print $fh '];';
$fh->close();
}
}
} else {
#
# if is from update, we store $self->{'SYM'} and $self->{'EXT'} from symbolcd.txt
#
$self->HashSymbolCD($symbolcd);
}
return;
#
# $Data::Dumper::Indent=1;
# $Data::T
# print Dumper($self->{'SYM'}, qw(sym)
}
#
# $obj->HashSymbolCD($file)
# - store symbolcd.txt($file) to $self->{'SYM'} hash and $self->{'EXT'} hash
#
# IN: $file - the full filespec of the symbolcd.txt
# OUT: none
#
sub HashSymbolCD
{
my ($self, $file) = @_;
my ($fh, $bin, $symbol, $subpath, $installpath, $kbterm);
local $_;
$kbterm = $pktypes{$self->{'PKTYPE'}};
$fh = new IO::File $file;
if (!defined $fh) {
logmsg "Cannot open symbolcd.txt ($file)";
return;
}
while(<$fh>) {
chomp;
($bin,$symbol,$subpath,$installpath)=split(/\,/,$_);
$self->{'SYM'}->{lc"$symbol\.$installpath"} = [$kbterm, "\\" . $subpath, (-s $self->{'KB'}->{$kbterm} . '\\' . $subpath), lc$installpath];
for (keys %pktypes) {
$self->{'EXT'}->{$_}->{lc$installpath} = 1;
}
}
$fh->close();
}
#
# $obj->HashArchServer($path)
# - store the list of symbols under $path to $self->{'SYM'} hash and $self->{'EXT'} hash
#
# IN: $path - the full path of the symbols (such as "\\\\arch\\archive\\ms\\windows\\windows_xp\\rtm\\2600\\$BuildType\\all\\$BuildArch\\pub")
# OUT: none
#
sub HashArchServer
{
my ($self, $path) = @_;
my ($fh, $bin, $symbol, $subpath, $installpath, $kbterm, $pdbsize);
local $_;
$kbterm = $pktypes{$self->{'PKTYPE'}};
$fh = new IO::File "dir /s/b/a-d $path\\*.*|";
if (!defined $fh) {
logmsg "Cannot access to $path\.";
}
while (<$fh>) {
chomp;
$pdbsize = (-s);
$_ = substr($_, length($path) + 1);
/\\/;
($symbol, $subpath, $installpath) = ($',$_,$`);
$self->{'SYM'}->{lc"$symbol\.$installpath"} = [$kbterm, '\\' . $subpath, $pdbsize, $installpath];
$self->{'EXT'}->{$self->{'PKTYPE'}}->{$installpath} = 1;
}
$fh->close();
}
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
#
# for Create_Symbols_CDF, and Create_Symbols_INF
#
# we create two writer, one is writing to update's and full package's files,
# another is only writing to full package's file
#
# so, when we call &{$mywriter{'FULL'} - it only write to full package's file
# when we call &{$mywriter{'UPDATE'} - it write to both files
#
# and, $mysepwriter is for writing separately
# $mysepwriter{'FULL'} - write to full package
# $mysepwriter{'UPDATE'} - write to update package
#
# we base on the writer function to loop our big symbols hash ($self->{'SYM'}) one time to
# generate two documents
#
# for Create_Symbols_DDF
#
# we only need to write each record to one target file (that either in full package, or update
# package to create symbols.cab). So, we don't use writer to write the DDF
#
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
#
# Create_Symbols_CDF($pkinfoptr)
# - Create symbols.CDF which is for creating the symbols.cat
#
# IN: $pkinfoptr-> # see RegisterPackage()
# $pkname -> # such as 'FULL' or 'UPDATE'
# 'CDFHANDLE' -> cdf file handle
# 'CATNAME' -> the symbols catalog file name
# 'INFNAME' -> the symbols inf file name
#
sub Create_Symbols_CDF
{
my ($self, $pkinfoptr) = @_;
my ($mykbterm, $mypkname, $fhandle, $fullpdb, %mywriter);
local $_;
&Open_Private_Handle($pkinfoptr, 'CDF');
for $mypkname (keys %{$pkinfoptr}) {
# create the writer
if ($mypkname ne 'FULL') {
$mywriter{$mypkname} = &Writer($pkinfoptr->{$mypkname}->{'CDFHANDLE'}, $pkinfoptr->{'FULL'}->{'CDFHANDLE'});
} else {
$mywriter{$mypkname} = &Writer($pkinfoptr->{'FULL'}->{'CDFHANDLE'});
}
# write the head to each cdf file handle
&Create_CDF_Head($pkinfoptr->{$mypkname}->{'CDFHANDLE'}, $pkinfoptr->{$mypkname}->{'CATNAME'}, $pkinfoptr->{$mypkname}->{'INFNAME'});
}
# write each record
for (sort keys %{$self->{'SYM'}}) {
$mykbterm = $self->{'SYM'}->{$_}->[0];
$mypkname = $revpktypes{$mykbterm};
$fullpdb = $self->{'KB'}->{$mykbterm} . $self->{'SYM'}->{$_}->[1];
&{$mywriter{$mypkname}}("\<HASH\>" . $fullpdb . '=' . $fullpdb . "\n");
}
&Close_Private_Handle($pkinfoptr, 'CDF');
}
#
# Create_Symbols_DDF($pkinfoptr)
# - Create symbols.DDF which is for creating symbols.cab
#
# IN: $pkinfoptr-> # see RegisterPackage()
# $pkname ->
# 'CABNAME' =>
# 'CABDEST' =>
# 'CABSIZE' =>
# 'DDFNAME' =>
# 'CABCOUNT' =>
# 'DDFLIST' => (return cab list)
# 'DDFHANDLE' =>
#
sub Create_Symbols_DDF
{
my ($self, $pkinfoptr) = @_;
my ($symkey, $symptr, $kbterm, $subpath, $pktype, $mypkinfoptr, $cabname, $ddfname, $cabcount, $DDFHandle, $myddfname, $mycabname);
local $_;
# initialization
map({$_->{'CURSIZE'} = $_->{'CABSIZE'}} values %{$pkinfoptr});
for (sort keys %{$self->{'SYM'}}) {
$symkey = $_;
$symptr = $self->{'SYM'}->{$_};
# base on $self->{'SYM'}->{$symbol\.$installpath}->[0] to reference %revpktypes and get the package type ($pktype)
($kbterm, $subpath) = ($symptr->[0],$symptr->[1]);
$pktype = $revpktypes{$kbterm};
# don't generate something not specify
next if (!exists $pkinfoptr->{$pktype});
$mypkinfoptr = $pkinfoptr->{$pktype};
#
# According uncompressed files total size to seperate the cab
# 'CURSIZE' - current size
# 'CABSIZE' - is the approx. size
#
$mypkinfoptr->{'CURSIZE'}+=$symptr->[2];
# if this cab list (ddf) is full
if ($mypkinfoptr->{'CURSIZE'} >= $mypkinfoptr->{'CABSIZE'}) {
# initial the current size to the current symbol's file size
$mypkinfoptr->{'CURSIZE'} = $symptr->[2];
($cabname, $ddfname, $cabcount) = (
$mypkinfoptr->{'CABNAME'},
$mypkinfoptr->{'DDFNAME'},
++$mypkinfoptr->{'CABCOUNT'}
);
$myddfname = $ddfname . $cabcount . '.ddf';
$mycabname = $cabname . $cabcount . '.cab';
# create the new file and its DDF file head
# the old file will automatically close by Perl
$mypkinfoptr->{'DDFHANDLE'} = new IO::File $myddfname, 'w';
if (!defined $mypkinfoptr->{'DDFHANDLE'}) {
logmsg "Cannot open DDF file $myddfname\.";
}
&Create_DDF_Head($mypkinfoptr->{'DDFHANDLE'}, $mycabname);
$mypkinfoptr->{'DDFLIST'}->{$myddfname} = $mycabname;
}
# write the record to the ddf file handle
$DDFHandle = $mypkinfoptr->{'DDFHANDLE'};
print $DDFHandle '"' . $self->{'KB'}->{$kbterm} . $subpath . '" "' . $symkey . "\"\n";
}
&Close_Private_Handle($pkinfoptr, 'DDF');
}
#
# Create_Symbols_INF($pkinfoptr)
# - Create symbols.inf
#
#
# pkinfoptr-> # see RegisterPackage()
# $pkname ->
# INFNAME =>
# CDFNAME =>
# CABNAME =>
# CATNAME =>
# INFHANDLE =>
#
sub Create_Symbols_INF
{
my ($self, $pkinfoptr) = @_;
my ($mypkname, $mypkinfoptr, $INFHandle, %mywriter, %mysepwriter, %h, %cabnames);
local $_;
&Open_Private_Handle($pkinfoptr, 'INF');
for $mypkname (keys %{$pkinfoptr}) {
($mypkinfoptr, $INFHandle) = ($pkinfoptr->{$mypkname}, $pkinfoptr->{$mypkname}->{'INFHANDLE'});
# create the writer
if ($mypkname ne 'FULL') {
$mywriter{$mypkname} = &Writer($INFHandle, $pkinfoptr->{'FULL'}->{'INFHANDLE'});
} else {
$mywriter{$mypkname} = &Writer($INFHandle);
}
$mysepwriter{$mypkname} = &Writer($INFHandle);
&Create_INF_Version($INFHandle, $mypkinfoptr->{'CATNAME'});
&Create_INF_Install($INFHandle, $self->{'EXT'}->{$mypkname});
$cabnames{$mypkname} = (FileParse($mypkinfoptr->{'CABNAME'}))[0];
}
# the extension tag (ex. [FILES.EXE]) needs to write separately depends on update / full package has this extension's symbols or not
# so, we need to use separately writer for this kind information
&Create_INF_Files($self->{'SYM'}, \%mysepwriter, \%mywriter);
&Create_INF_SourceDisks($self->{'SYM'}, \%cabnames, \%mysepwriter, \%mywriter);
&Close_Private_Handle($pkinfoptr, 'INF');
}
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
#
# Create_DDF_Head - create the .ddf's file head for symbols(n).ddf
# Create_CDF_Head - create the .cdf's file head for symbols.cdf
# Create_INF_Version - create the .inf's Version section
# Create_INF_Install - create the .inf's [DefaultInstall], [DefaultInstall.Quiet]
# [BeginPromptSection], [EndPromptSection], [RegVersion]
# [SymCust], [CustDest], [CustDest.2], [DestinationDirs]
#
# Create_INF_Files - create the .inf's [Files.<extension>] sections
# Create_INF_SourceDisks - create the [SourceDisksNames], [SourceDisksFiles]
#
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
sub Create_DDF_Head
{
my ($DDFHandle, $cabname) = @_;
my ($mycabname, $mycabdest) = FileParse($cabname);
print $DDFHandle <<DDFHEAD;
.option explicit
.Set DiskDirectoryTemplate=$mycabdest
.Set RptFileName=nul
.Set InfFileName=nul
.Set CabinetNameTemplate=$mycabname\.cab
.Set CompressionType=MSZIP
.Set MaxDiskSize=CDROM
.Set ReservePerCabinetSize=0
.Set Compress=on
.Set CompressionMemory=21
.Set Cabinet=ON
.Set MaxCabinetSize=999999999
.Set FolderSizeThreshold=1000000
DDFHEAD
}
sub Create_CDF_Head
{
my ($CDFHandle, $catname, $infname) = @_;
$catname = (FileParse($catname))[0];
print $CDFHandle <<CDFHEAD;
[CatalogHeader]
Name=$catname
PublicVersion=0x00000001
EncodingType=0x00010001
CATATTR1=0x10010001:OSAttr:2:5.X
[CatalogFiles]
\<HASH\>$infname\.inf=$infname\.inf
CDFHEAD
}
sub Create_INF_Version
{
my ($INFHandle, $catname) = @_;
$catname = (FileParse($catname))[0];
print $INFHandle <<INFVERSION;
[Version]
AdvancedInf= 2.5
Signature= "\$CHICAGO\$"
CatalogFile= $catname\.CAT
INFVERSION
}
sub Create_INF_Install
{
my ($INFHandle, $exthptr) = @_;
my $CopyFiles = 'Files.' . join(", Files\.", sort keys %{$exthptr});
print $INFHandle <<INF_INSTALL;
[DefaultInstall]
CustomDestination= CustDest
AddReg= RegVersion
BeginPrompt= BeginPromptSection
EndPrompt= EndPromptSection
RequireEngine= Setupapi;
CopyFiles= $CopyFiles
[DefaultInstall.Quiet]
CustomDestination=CustDest.2
AddReg= RegVersion
RequireEngine= Setupapi;
CopyFiles= $CopyFiles
[BeginPromptSection]
Title= "Microsoft Windows Symbols"
[EndPromptSection]
Title= "Microsoft Windows Symbols"
Prompt= "Installation is complete"
[RegVersion]
"HKLM","SOFTWARE\\Microsoft\\Symbols\\Directories","Symbol Dir",0,"\%49100\%"
"HKCU","SOFTWARE\\Microsoft\\Symbols\\Directories","Symbol Dir",0,"\%49100\%"
"HKCU","SOFTWARE\\Microsoft\\Symbols\\SymbolInstall","Symbol Install",,"1"
[SymCust]
"HKCU", "Software\\Microsoft\\Symbols\\Directories","Symbol Dir","Symbols install directory","\%25\%\\Symbols"
[CustDest]
49100=SymCust,1
[CustDest.2]
49100=SymCust,5
[DestinationDirs]
;49100 is \%systemroot\%\\symbols
Files.inf = 17
Files.system32 = 11
INF_INSTALL
for (sort keys %{$exthptr}) {
printf $INFHandle ("Files\.%-6s\t\t\= 49100,\"%s\"\n", $_, $_);
}
}
sub Create_INF_Files
{
my ($symptr, $sepwriter, $popwriter) = @_;
my ($mykbterm, $mypkname, %tags);
local $_;
for (sort {($symptr->{$a}->[3] cmp $symptr->{$b}->[3]) or ($a cmp $b)} keys %{$symptr}) {
$mykbterm = $symptr->{$_}->[0];
$mypkname = $revpktypes{$mykbterm};
# if is a new tag name,
if ($symptr->{$_}->[3] ne $tags{$mypkname}->[0]) {
$tags{$mypkname} = [$symptr->{$_}->[3], - length($symptr->{$_}->[3]) -1];
&{$sepwriter->{$mypkname}}("\n\[Files\.$tags{$mypkname}->[0]\]\n");
}
# if its from update list and different with the current full list's file extension
# we need to create a new tag in full list
if ($symptr->{$_}->[3] ne $tags{'FULL'}->[0]) {
$tags{'FULL'} = [$symptr->{$_}->[3], - length($symptr->{$_}->[3]) -1];
&{$sepwriter->{'FULL'}}("\n\[Files\.$tags{'FULL'}->[0]\]\n");
}
# $popwriter will write update ($mypkname) to both files and full to full package list
&{$popwriter->{$mypkname}}(substr($_, 0, $tags{$mypkname}->[1]) . "\,$_\,\,4\n");
}
}
sub Create_INF_SourceDisks
{
my ($symptr, $cabnameptr, $sepwriter, $popwriter) = @_; # $pkinfoptr) = @_;
my ($INFHandle, $cabname, $mypkname);
local $_;
for (keys %{$cabnameptr}) {
$cabname = $cabnameptr->{$_};
&{$sepwriter->{$_}}(<<SOURCE_DISKS);
[SourceDisksNames]
1="$cabname\.cab",$cabname\.cab,0
[SourceDisksFiles]
SOURCE_DISKS
}
for (sort keys %{$symptr}) {
$mypkname = $revpktypes{$symptr->{$_}->[0]};
&{$popwriter->{$mypkname}}($_ . "=1\n");
}
}
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
#
# Reusable subruntines
#
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
#
# RegisterPackage($pkinfoptr, $pktype,$hptr)
# - register $hptr to $pkinfoptr->{$pktype}
# - registerpackage is the function to verify and store $pkinfoptr
# - the cabname, ddfname, infname, cdfname, catname - are equal to 'SYMBOLS' normally
# - cabsize is the total size for include files into one cab, we cannot compute the compressed
# file size, so we compute with the uncompress file size
#
# - cabhandle, ddfhandle, infhandle - the file handles for .cab, .ddf and .inf; we create them
# inside this module
# - DDFLIST - the lists of the ddf files we plan to create
#
# $pkinfoptr->
# $pktype ->
# CABNAME
# DDFNAME
# INFNAME
# CDFNAME
# CATNAME
# CABSIZE
#
# CABHANDLE - (reserved)
# DDFHANDLE - (reserved)
# INFHANDLE - (reserved)
#
# DDFLIST - (return)
#
sub RegisterPackage
{
my ($pkinfoptr, $pktype, $hptr) = @_;
my ($mykey);
my @chklists = qw(CABNAME DDFNAME INFNAME CDFNAME CATNAME CABSIZE);
# register to $pkinfoptr
$pkinfoptr->{$pktype} = $hptr;
# check we have enough information
for $mykey (@chklists) {
die "$mykey not defined in $pktype" if (!exists $pkinfoptr->{$pktype}->{$mykey});
}
}
#
# $obj = Writer(@handles)
# - the writer generator; generates a function to write one data to each @handles
# keep @handles in parent program and $_[0] in sub {....} is the data we send to &{$obj}($data)
#
sub Writer {
my (@handles) = @_;
my ($hptr)=\@handles;
return sub {
my ($myhandle);
for $myhandle (@{$hptr}) {
print $myhandle $_[0];
}
};
}
#
# Open_Private_Handle($pkinfoptr, $ftype)
# - open each pkname of each $ftype name's file and store the handle to $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'}
# - for example, if we call Open_Private_Handle($pkinfoptr, 'DDF')
# - it open $pkinfoptr->{'FULL'}->{'DDFNAME'} and $pkinfoptr->{'UPDATE'}->{'DDFNAME'}
# - and store the file handle to $pkinfoptr->{'FULL'}->{'DDFHANDLE'} and $pkinfoptr->{'UPDATE'}->{'DDFHANDLE'}
#
sub Open_Private_Handle
{
my ($pkinfoptr, $ftype) = @_;
my ($pkname);
for $pkname (keys %{$pkinfoptr}) {
$pkinfoptr->{$pkname}->{$ftype . 'HANDLE'} = new IO::File $pkinfoptr->{$pkname}->{$ftype . 'NAME'} . '.' . $ftype, 'w';
if (!defined $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'}) {
logmsg "Cannot open " . $pkinfoptr->{$pkname}->{$ftype . 'NAME'} . '.' . $ftype . ".";
}
}
}
sub Close_Private_Handle
{
my ($pkinfoptr, $ftype) = @_;
my ($pkname);
for $pkname (keys %{$pkinfoptr}) {
$pkinfoptr->{$pkname}->{$ftype . 'HANDLE'}->close() if (defined $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'});
delete $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'};
}
}
sub FileParse
{
my ($name, $path, $ext) = fileparse(shift, '\.[^\.]+$');
$ext =~ s/^\.//;
return $name, $path, $ext;
}
1;