|
|
# ///////////////////////////////////////////////////////////////////////////////////////////// # Method calling graph # -------------------- # -> ParseCmdLine -> PrintHelp # -> AddTarget # -> CleanLogFiles # -> GetSysdir # -> LoadEnv # -> LoadHotFix # -> LoadCodes # -> LoadReposit -> LoadKeys -> SetField # -> IsHashKey # -> AddTargetPath # -> LoadCmd -> IsRepositKey # -> PushFieldVal # -> ReadSysfile(R) -> ReplaceVar # -> SetIfFlag -> ReplaceVar # -> SetAttribOp -> ReplaceVar # -> GetMappingType # -> ReadBlock -> SetIfFlag -> ReplaceVar # -> SetAttribOp -> ReplaceVar # -> ReplaceVar # -> ReadLine -> ReplaceVar # -> LoadRecord -> LineToRecord # -> IsLangOK # -> AddRecord(R) -> LogRecord # -> VerifyCover -> Create_Tree_Branch -> Get_Except # -> Remove_Dummy_Create_Branch # -> Remove_Root_List # -> Find_UnMapping # -> PopulateReposit -> FillFilter -> IsEmptyHash # -> AddFiles -> AddEntry -> IsEmptyHash # -> IsHashKey # -> AddFileInfo -> IsRepositKey # -> IsHashKey # -> AddTargetPath # -> SetField # -> IsEmptyHash # -> IsHashKey # -> IsFoundTarget # -> FillTokens -> IsRsrcTok # -> FillTokEntry -> IsEmptyHash # -> IsHashKey # -> FillTokInfo -> ExistsBinForTok -> IsRepositKey # -> GetFieldVal # -> IsRepositKey # -> GetFieldVal # -> SetField # -> IsBgnTok # -> fullnamevalue # -> TokToFile -> IsRsrcTok # -> IsBgnTok # -> IsCustRes # -> FillCmd -> IsEmptyHash # -> IsHashKey # -> GetFieldVal # -> RecordToCmd -> IsXcopyCmd -> GetFieldVal # -> GenXcopy -> ImageToSymbol # -> GetFieldVal # -> SetSymPaths # -> MakeXcopyCmd # -> PushFieldVal # -> IsLocCmd -> GetFieldVal # -> GenLocCmd -> IsBgnOp -> IsBgnTok # -> GetFieldVal # -> GenBgnCmd -> ImageToSymbol # -> GetFieldVal # -> SetSymPaths # -> MakeXcopyCmd # -> GetLocOpType # -> SetBgnSymSw # -> GetLangCodes # -> GetBgnCodePageSw # -> GetBgnICodesSw -> GetLangCodes # -> GetBgnMultitok -> GetFieldVal # -> DeleteKey # -> GetBgnSw # -> PushFieldVal # -> IsRsrcOp -> IsRsrcTok # -> GenRsrcCmd -> ImageToSymbol # -> GetFieldVal # -> SetSymPaths # -> MakeXcopyCmd # -> GetLocOpType # -> GetLangNlsCode -> GetLangCodes # -> SetRsrcSymSw # -> PushFieldVal # -> GetFieldVal # -> IsRepositKey # -> GenMUICmd -> GetLangNlsCode -> GetLangCodes # -> GetFieldVal # -> PushFieldVal #------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # SYNTAX_CHECK_ONLY -> SumErrors -> exit #------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # -> GenNmakeFiles -> UpdTargetEntries -> IsEmptyHash # -> DeleteKey # -> SetField # -> GetFieldVal # -> FillSyscmd -> IsEmptyHash # -> CmdCompare -> GetFieldVal # -> GenMakeDir -> SetField # -> PushFieldVal # -> FixRepository # -> WriteToMakefile -> WriteSysgenTarget -> IsEmptyHash # -> WriteAllTarget -> IsEmptyHash # -> GetFieldVal # -> WriteFileCmds -> IsEmptyHash # -> WriteToSyscmd -> IsEmptyHash # -> SumErrors # //////////////////////////////////////////////////////////////////////////////////////////////
use Data::Dumper;
my $DEBUG=0; my $DEBUG_SYMBOLCD=0;
my $PARSESYMBOLCD = 0; my $SYNTAX_CHECK_ONLY = undef; my $FULLCHECK = undef; my $DLINE = "RIGHT : LEFT";
# Global Constants
# MAP_ERR: Errors SYSGEN recognizes # 1101 is used for ERROR instructions found in sysfiles
require 5.003;
use lib $ENV{ "RazzleToolPath" }; use cklang;
%MAP_ERR = ( 1001, "sysfile not found", 1002, "file not found", 1003, "file not found: unable to run sysgen incrementally", 1004, "target file not found", 1005, "filter file not found", 1006, "unable to open file for reading", 1007, "unable to open file for writing", 1008, "/F option requires a filename", 1009, "target not found in filter file",
1101, "",
1110, "syntax error", 1111, "syntax error: ENDIF unexpected", 1112, "syntax error: IF unexpected", 1113, "syntax error: END_MAP unexpected", 1114, "syntax error: BEGIN_*_MAP unexpected", 1115, "syntax error: INCLUDE unexpected", 1116, "syntax error: unknown operator in expression", 1117, "syntax error: \")\" missing in macro invocation", 1118, "syntax error: incomplete description line", 1119, "syntax error: unknown mapping type", 1120, "syntax error: unmatched IF", 1121, "syntax error: unmatched BEGIN_*_MAP",
1210, "file format error: target not found", 1211, "file format error: target not listed in \"all\"", 1212, "file format error: no description blocks found for files", 1213, "file format error: \"sysgen\" target not found", 1214, "file format error: filename with special characters", 1215, "file format error: Similar target found",
1910, "unknown language", 1911, "missing or duplicated entry for language", 1912, "incomplete entry for language", 1913, "unknown class for language",
2011, "no binary found for token", 2012, "duplicated tokens", 2013, "unknown token type (bingen or rsrc)", 2014, "unexpected token for already localized binary", 2015, "input bingen token not found for multi", 2016, "no bingen token found for custom resource", 2017, "unknown bingen token extension in token filename", 2018, "both bingen and rsrc tokens found", 2019, "custom resource found for rsrc token", 2020, "custom resource with no token",
2051, "sysfile error: undefined source path to verify", 2052, "folder not covered in sysfiles",
2101, "binary not found", 2102, "token not found",
3011, "internal error: unknown operation type",
4001, "filename with whitespace not aggregated",
); # MAP_ERR
# MAP_BINGEN_TOK_EXT: Bingen token extensions SYSGEN recognizes
%MAP_BINGEN_TOK_EXT =( '.a_', '.ax', '.ac_', '.acm', '.ax_', '.ax', '.bi_', '.bin', '.cn_', '.cnv', '.co_', '.com', '.cp_', '.cpl', '.dl_', '.dll', '.dr_', '.drv', '.ds_', '.ds', '.ef_', '.efi', '.ex_', '.exe', '.fl_', '.flt', '.im_', '.ime', '.mo_', '.mod', '.ms_', '.mst', '.oc_', '.ocx', '.rs_', '.rsc', '.sc_', '.scr', '.sy_', '.sys', '.tl_', '.tlb', '.ts_', '.tsp', '.wp_', '.wpc',
); # MAP_BINGEN_TOK_EXT
# MAP_RSRC_TOK_EXT: Rsrc token extensions SYSGEN recognizes
%MAP_RSRC_TOK_EXT =( '.rsrc', '',
); # MAP_RSRC_TOK_EXT
# CODESFNAME - the name of the file containing the language codes for all languages # SYSGEN looks for this file in %NT_BLDTOOLS%
$CODESFNAME = "codes.txt";
# MAPPINGS: Mapping types SYSGEN recognizes
@MAPPINGS = ("FILES", "BINDIRS", "TOKENS", "TOKDIRS");
# FFILES, FBINDIRS, FTOKENS, FTOKDIRS: The corresponding list of fields
@FFILES = ( "DestPath", "DestFile", "DestDbg", "SrcPath", "SrcFile", "SrcDbg", "Xcopy", "Langs" ); @FBINDIRS = ( "DestPath", "DestDbg", "SrcPath", "SrcDbg", "Xcopy", "R", "Langs"); @FTOKENS = ( "DestPath", "DestFile", "TokPath", "TokFile", "ITokPath", "LocOp", "BgnSw", "Langs"); @FTOKDIRS = ( "DestPath", "TokPath", "ITokPath", "LocOp", "BgnSw", "Langs");
# Localization Tool Operation Types (append or replace)
%LOCOPS = ( a => '-a', r => '-r', i => '-i $opts', ai => '-i $opts -a', ri => '-i $opts -r' );
# Bingen Switches
@BGNSWS = ( "-b", "-c" );
# Error and Log Filenames $LOGFILE = "sysgen.log"; $ERRFILE = "sysgen.err";
# Global Variables
# in addition to the if_flag, allow for nested IFs; @IFSTACK = ();
# CODES - the contents of the CODESFNAME file, change to hash at 04/03/00 %CODES = ();
# TFILES, TBINDIRS, TTOKENS, TTOKDIRS: Records for the corresponding mapping (one of @MAPPINGS), # in the same order as they are found in the mapping files. # One record is a hash with the keys stored by @F<mapping_type>
@TFILES = (); @TBINDIRS = (); @TTOKENS = (); @TTOKDIRS = ();
#
%VfyPath = ( Exclude_Root => 'DIR_PATHS', Exclude_Subdir => 'IGNORE_PATHS', Exclude_File_Pattern => '(\.pdb)|(\.edb)|(\.dbg)|(slm\.ini)|(symbols)' );
# REPOSITORY: Info necessary to generate the appropriate xcopy or bingen command, # for every file to be aggregated.
%REPOSITORY = (); # As a db table, the key is (DestPath, DestFile). # As a Perl hash: # key = $DestPath (lowercase) # value = hash with # key = $DestFile (lowercase) # value = hash with # keys = DestPath, DestFile, DestDbg, # SrcPath, SrcFile, SrcDbg, # TokPath, TokFile, ITokPath, # OpType, LocOp, BgnSw, # Cmd # # not necessarily all of them defined.
my %REPOSITORY_TEMPLATE = map {$_=>1} qw( SrcFile DestFile SrcDbg DestDbg SrcPath DestPath LocOp BgnSw); # CMD_REPOSITORY: Repository as found in an existing SYSGEN-generated makefile. # For every key, only {Cmd} field is filled in.
%CMD_REPOSITORY = ();
# SYSCMD: Stores the contents of the syscmd (SYSGEN-generated NMAKE command file)
@SYSCMD= ();
# MACROS: Hash storing the names and values of the macros. # Types of macros sysgen recognizes: # - command line macros (type 1)= macros defined in the command line. # Their value can be overwritten only by another command line macro. # - sysfile macros (type 0) = macros defined by the sysfiles, # inside or outside the pair (BEGIN_*_MAP, END_MAP) (type 0). # The sysfile macros do not overwrite the command line macros, but they overwrite the environment # macros. # - environment macros (type 0 too) = variables inherited from the cmd environment. # Can be overwritten by the other types of macros. # Internally, MACROS uppercases all the macro names, so the macronames are case insesitive.
%MACROS = ();
# FILTER # Stores the list of files to be considered.
%FILTER = ();
# FILE: Filename of the current description file; LINE: the current line number
$FILE = ""; $LINE = 0;
# ERRORS: number of non fatal errors found during running SYSGEN
$ERRORS = 0;
# SYSFILE: names of the starting description files # By default, SYSFILE is set to ".\\sysfile" if not specified otherwise in the command line, # /F option $HOTFIX = "hotfix"; $SYSFILE = ".\\sysfile";
# TARGETS: targets specified in the command line
%TARGETS = (); # key = $DestFile (lowercase string)) # value = hash with # keys = OldDestPath (array), NewDestPath (array)
%MAKEDIR=(); %hotfix = ();
# CLEAN : 1 if a clean SYSGEN is invoked ( by specifying /c in the command line )
$CLEAN = 0;
# SECTIONS
($SECTIONNAME, $DEFAULT_SECTIONS, $SECTIONS)=("process");
# main {
# Flush output buffer for remote.exe select(STDOUT); $| = 1;
# Parse the command line &ParseCmdLine(@ARGV);
# Verify SYSFILE's existence ( -e $SYSFILE ) || &FatalError( 1001, $SYSFILE ); $FULLCHECK = &GetMacro("FULLCHECK") unless $FULLCHECK; # Clean sysgen.err and sysgen.log &CleanLogFiles( &GetSysdir( $SYSFILE ) );
# Load the inherited environment variables &LoadEnv();
&LoadHotFix(join("\\", &GetSysdir( $SYSFILE ), $HOTFIX ));
# Load codes.txt file &LoadCodes();
# For incremental, load Repository-associated # commands from an existing makefile to CMD_REPOSITORY. &LoadReposit( sprintf( "%s\\makefile", &GetSysdir( $SYSFILE ) ) );
# Read the description files &ReadSysfile( $SYSFILE );
# Verify the whole tree covered. &VerifyCover();
# Populate the repository (according to the description files) &PopulateReposit();
if ($SYNTAX_CHECK_ONLY){ # Exit now since # no more errors can be generated. &SumErrors(); exit 0; } # &SumErrors() and exit if $SYNTAX_CHECK_ONLY; # # no more errors can be generated.
&GenNmakeFiles( &GetSysdir( $SYSFILE ) );
# Display errors &SumErrors();
# } # main
# ReadSysfile # Reads a given sysfile and all the included sysfiles; # For each mapping type found, loads data into the corresponding array of hashes # ( one of the "@T" variables ) # Input # mapping filename # Output # none # Usage # &ReadSysfile( "sysgen.def" );
sub ReadSysfile {
my $mapfname = shift;
# File contents my @mapfile = (); @IFSTACK = (); # Mapping type ( one of @MAPPINGS ) my $maptype;
# Included filename my $incfile;
# Flags my ( $if_flag, $inside_if, $if_line ) = (1, 0, undef);
# Indexes my $i;
# Error text my $error;
# Open the mapping file ( -e $mapfname ) || &FatalError( 1002, $mapfname ); print "Loading $mapfname...\n";
if ($PARSESYMBOLCD && !(%default) && &GetMacro("SYMBOLCD")){
%default = parseSymbolCD(&GetMacro("SYMBOLCD")); map {print STDERR $_ , "\n"} values(%default) if $DEBUG; }
# Load file contents open( FILE, $mapfname ) || &FatalError( 1006, $mapfname ); @mapfile = <FILE>; close( FILE );
# Parse the mapping file and load the mappings. for ( $i=0, $LINE=0, $FILE=$mapfname; $i < @mapfile; $i++, $LINE++ ) {
for ( $mapfile[$i] ) {
SWITCH: {
# white lines if ( ! /\S/ ) { last SWITCH; }
# commented lines if ( /^\s*#/ ) { last SWITCH; }
# ENDIF if ( /\bENDIF\b/i ) {
scalar(@IFSTACK) or &FatalError( 1111, $FILE, $LINE ); pop @IFSTACK; $inside_if = scalar(@IFSTACK); $if_flag = ($inside_if) ? $IFSTACK[-1] : 1; last SWITCH; } # case
# IF if ( /\bIF\b/i) { push @IFSTACK, &SetIfFlag( $_); $if_flag and $if_flag = $IFSTACK[-1]; $inside_if = 1; $if_line = $i; last SWITCH; } # case
if ( ! $if_flag ) { last SWITCH; }
# INCLUDE
if ( /\bINCLUDE\b\s+(\S+)/i ) { # Evaluate the macros of the include statement $incfile = &ReplaceVar( $1, 0 ); # Read the included file &ReadSysfile($incfile); # Continue with the current file; set FILE and LINE back $FILE = $mapfname; $LINE = $i; last SWITCH; } # case
# SET if ( /\bSET\b/i ) {
&SetMacro( &SetAttribOp( $_ ), 0 ); last SWITCH; } # case
# ERROR if ( /\bERROR\b\s+(\S+)/i) { $error = &ReplaceVar( $1, 0 ); &FatalError( 1101, $error ); last SWITCH; } # case
# END_MAP if ( /END_MAP/i ) {
&FatalError( 1113, $FILE, $LINE ); last SWITCH; } # case
# BEGIN_MAP if ( /\bBEGIN_(\S+)_MAP/i ) {
$maptype = &GetMappingType( $_ );
( $maptype ) || &FatalError( 1119, $FILE, $LINE );
# Read the found map. print "Loading $MAPPINGS[$maptype-1] map from $mapfname...\n";
# Read the map and return the last line index
$i = &ReadBlock( \@mapfile, $i+1, $maptype-1 ); last SWITCH; } # case
# default &FatalError( 1110, $FILE, $LINE );
} #SWITCH } #for
} # for
&FatalError( 1120, $FILE, $if_line ) if scalar(@IFSTACK);
return;
} # ReadSysfile
# GetMappingType # Identifies the mapping type in a BEGIN_MAP statement # Input # BEGIN_MAP statement # Output # mapping index or 0 if the mapping type is unknown
sub GetMappingType { my %REVERSE = map {$MAPPINGS[$_]=>$_+1} 0..$#MAPPINGS; my $test = shift; chomp $test; $test =~ s/\s+$//gm; $test =~ s/BEGIN_(\w+)_MAP/\U$1/; $REVERSE{$test}; } # GetMappingType
# ReadBlock # Reads a map from the mapping file, according to its type. # Loads data into the corresponding "table" variable ( one of the "@T" variables) # Input # sysfile contents (reference) # index of the current line # mapping type # Output # index of the END_MAP line # Usage # $index = &ReadBlock( \@mapfile, $index, $maptype );
sub ReadBlock {
my($mapref, $index, $maptype) = @_; my $ifstack = scalar(@IFSTACK); # Output - $index
# Other indexes my $line;
# IF flags my( $if_flag, $inside_if, $if_line ) = (1, 0, undef);
# Error text my $error;
# Parse the current mapping type for ( ; $index < @{$mapref}; $index++, $LINE++ ) {
for ( $mapref->[$index] ) { SWITCH: {
# white lines if ( ! /\S/ ) { last SWITCH; }
# commented lines if ( /^\s*#/ ) { last SWITCH; }
# ENDIF if ( /\bENDIF\b/i) {
scalar(@IFSTACK) or &FatalError( 1111, $FILE, $LINE ); pop @IFSTACK; $inside_if = scalar(@IFSTACK); $if_flag = ($inside_if) ? $IFSTACK[-1] : 1; last SWITCH; } # case
if ( /\bIF\b/i) { push @IFSTACK, &SetIfFlag( $_); $if_flag and $if_flag = $IFSTACK[-1]; $inside_if = 1; $if_line = $i; last SWITCH; } # case
if ( ! $if_flag ) { last SWITCH; }
# INCLUDE if ( /\bINCLUDE\b\s+(\S+)/i ) {
&FatalError( 1115, $FILE, $LINE ); last SWITCH; } # case
# SET if ( /\bSET\b/i ) {
&SetMacro( &SetAttribOp( $_ ), 0 ); last SWITCH; } # case
# ERROR if ( /\bERROR\b\s(\S+)/i) { $error = &ReplaceVar( $1, 0 ); &FatalError( 1101, $error ); last SWITCH; } # case
# END_MAP if ( /END_MAP/i ) { ( $ifstack!= scalar(@IFSTACK) ) and &FatalError( 1120, $FILE, $if_line ); return $index; } # case
# BEGIN_MAP if ( /BEGIN_\S+_MAP/i ) {
&FatalError( 1114, $FILE, $LINE ); last SWITCH; } # case
# default &ReadLine( $mapref, $index, $maptype );
} # SWITCH } # for
} # for
print &FatalError( 1121, $FILE, $LINE ); return -1;
} # ReadBlock
# SetIfFlag # Evaluates the given IF expression. # Input # IF statement # Output # 1 if the IF expression is satisfied, 0 otherwise
sub SetIfFlag {
my( $entry ) = @_;
# Output my $ret=0;
# Store entry my $line = $entry;
# Operator my $operator;
# Operands my($operand1, $operand2);
$entry = &ReplaceVar( $entry, 0 );
# Identify the operands and the operator, then evaluate the expression. $entry =~ /\s*IF\s+(\S*)\s+(\S*)\s+(\S*)\s*/i;
# Set operator $operand1 = $1; $operator = $2; $operand2 = $3;
SWITCH: { if ( $operator eq "==" ) { if ( $operand1 eq $operand2 ) { $ret = 1; }
last SWITCH; } # if
if ( $operator eq "!=" ) { if ( $operand1 ne $operand2 ) { $ret = 1; }
last SWITCH; } # if
&FatalError( 1116, $FILE, $LINE );
} # SWITCH
return $ret;
} # SetIfFlag
# SetAttribOp # Evaluates a SET statement. # Input # a SET statement # Output # evaluated macroname and macrovalue # Usage # ($macroname, $macrovalue) = &SetAttribOp( $mapfile[$index] ) ;
sub SetAttribOp {
my( $entry ) = @_;
# Output my($varname, $value);
$entry = &ReplaceVar( $entry, 0 ); $entry =~ /SET\s*(\S*)\s*=\s*\;*(.*)\s*/i;
# Set variable's name and variable's value $varname = $1; $value = $2;
# Verify if a variable name is defined if ( $varname eq "" ) { return ("", ""); } # if
return ($varname, $value);
} # SetAttribOp
# ReadLine # Description # Reads one description line, evaluates it and store the records it generates # Input # sysfile contents ( reference ) # line number # Output # none # Usage # &ReadLine( $mapref, $index, $maptype ); # Implementation
sub ReadLine {
my( $mapref, $index, $maptype ) = @_;
# Array of records my @records;
# Indexes my $i;
# Replace variables and spawn new records @records = &ReplaceVar( $mapref->[$index], 1 );
for ( $i=0; $i < @records; $i++ ) { &LoadRecord( $maptype, $records[$i] ); } # for
return $index;
} # ReadLine
# LoadRecord # Description # Transforms an evaluated description line into a record, # and adds it to the appropriate table. # Input # mapping type # description line (string) # Output # <none> # Usage # &LoadRecord( $maptype, $line_contents );
sub LoadRecord {
my($maptype, $entry) = @_;
# The hash table storring the current line (record). # The keys (field names) are given by the corresponding "@F$maptype" array. my %record;
%record = &LineToRecord( $maptype, $entry );
# If the current language matches the record language, # add the record to the corresponding "@T$maptype" array.
if ( &IsLangOK( $record{Langs}, &GetMacro( "language" ) ) ) { &AddRecord( $maptype, %record ); } elsif ((!defined $record{SrcFile}) && (defined $record{SrcPath})) { print "Ignore $record{SrcPath}\n"; &SetMacro( $VfyPath{'Exclude_Subdir'}, &GetMacro($VfyPath{'Exclude_Subdir'}) . "; " . $record{SrcPath}, 0 ); }# if
return;
} # LoadRecord
# LineToRecord # Transforms a given string into a record (hash table). # Input # mapping type # description line contents # Output # record of the mapping type # Usage # %record = &LineToRecord( $maptype, $entry );
sub LineToRecord {
my($maptype, $entry) = @_;
# Output my %record;
# Mapping name my $mapname;
# Corresponding "@F$maptype" variable (reference) my $ftable;
# Current line split in fields my @fields;
# Storage for the current line my $line = $entry;
# Indexes my($i, $j);
# Get the mapping name. $mapname = "F$MAPPINGS[$maptype]"; $ftable = \@$mapname;
# Split the record into fields @fields = split " ", $entry;
# Fill in %record variable with the field names as keys and # the field values as values. for ( $i=0; $i< @$ftable; $i++ ) {
# All fields must be non-null if ( !$fields[$i] ) { &FatalError( 1118, $FILE, $LINE ); } # if $record{$ftable->[$i]} = $fields[$i]; } # for
return %record;
} # LineToRecord
# AddRecord # Adds the given record to the appropriate "@T" table. # Input # mapping type # record # Output # <none>
sub AddRecord {
my($maptype, %record) = @_;
# Table name ( name of one of the "@T" variables ) my $tname;
# Table (reference) my $table;
# Storage for the current record my %buffer;
# Subdirectories ( if "R" field defined ) my @subdirs;
# Source field name (one of SrcPath, TokPath ) my $fname;
# Indexes my $i;
# Set table $tname = "T$MAPPINGS[$maptype]"; $table = \@$tname;
# Set the field name for the path ( TokPath or SrcPath) # according to the mapping type SWITCH: {
if ( $MAPPINGS[$maptype] =~ /TOK/i ) { $fname = "TokPath"; last SWITCH; } # if
if ( $MAPPINGS[$maptype] =~ /BIN/i ) { $fname = "SrcPath"; last SWITCH; } # if
if ( $MAPPINGS[$maptype] =~ /FILES/i ) { $fname = "SrcPath"; last SWITCH; } # if
# default # nothing to do
} # SWITCH
# Verify the existence of the source path if ( ! -e $record{$fname} ) { # print "Warning: $MAPPINGS[$maptype] map: No source directory $record{$fname}\n"; return; } # if
# Insert record into the table push @$table, \%record;
# Write the record to the log file &LogRecord($maptype, \%record);
# For Bindirs, look for the value of Subdirs if ( $MAPPINGS[$maptype] ne "BINDIRS" ) { return; } # if
# Return if no subdirectories to be parsed ( "R" field set to "n" ). if ( $record{R} =~ /n/i ) { return; } # if
# Get the list of subdirectories @subdirs = `dir /ad /b $record{SrcPath}`; # Add one record for each subdirectory found. for ( $i=0; $i < @subdirs; $i++ ) {
chomp $subdirs[$i];
# Found that in NT5, the result of dir /b starts with an empty line if ( $subdirs[$i] eq "" ) { next; }
# Skip the "symbols" subdirectory if ( $subdirs[$i] eq "symbols" ) { next; }
# Add one record for the current subdirectory %buffer = %record; $buffer{DestPath} .= "\\$subdirs[$i]"; $buffer{SrcPath} .= "\\$subdirs[$i]";
&AddRecord( $maptype, %buffer );
} # for
return;
} # AddRecord
# LogRecord # Writes the record to the log file. # Input # mapping type (reference) # record (reference) # Output # <none>
sub LogRecord {
my( $maptype, $recref ) = @_;
# String to be written to the logfile my $logstr;
$logstr = "$MAPPINGS[$maptype]:";
for ( sort keys %{$recref} ) {
$logstr .= "$_=$recref->{$_} ";
} # for
$logstr .= "\n"; # print "$logstr";
} # LogRecord
# ReplaceVar # Replaces all the macronames with their value in the given string. # Input # the string to be evaluated # the replacement type: # 0 = scalar # 1 = array # Output # array if the replacement type is 1 # string if the replacement type is 0 # In both cases, all the macros are evaluated. # Usage # $entry = &ReplaceVar( $entry, 0 ); # @records = &ReplaceVar( $entry, 1 );
sub ReplaceVar {
my( $line, $type ) = @_; $line =~ s/\s{2,}/ /g; $line =~ s/^\s//; $line =~ s/\s$//;
my @list = ($line);
foreach $line (@list){ my $epyt = 0;
while ($line =~ /\$\(([^\)]+)\)/){ my $macro = $1; $line =~ s/\$\(\Q$macro\E\)/__MACRO__/g;
if ($macro =~ /(\w+)\[\s?\]/) { $macro = $1; $epyt = 1; }
my $thevalue = &GetMacro($macro); $thevalue =~ s/\s+//g;
if (!$epyt){ $line =~ s/__MACRO__/$thevalue/g; } else{ pop @list; # take one, give many foreach my $value (split (";" , $thevalue)){ next if $value !~ /\S/; my $this = $line; $this =~ s/__MACRO__/$value/g; push @list, $this; } } } }
($type) ? @list : pop @list; } # ReplaceVar
# LoadCodes # Loads $CODEFNAME's file contents in %CODES # Input & Output <none> # Usage # &LoadCodes();
sub LoadCodes {
# codes.txt filename my $codefile;
my @items;
my $code;
# Get the contents of $CODESFNAME $codefile = sprintf "%s\\$CODESFNAME", &GetMacro( "RazzleToolPath" ); ( -e $codefile ) || &FatalError( 1002, $codefile );
( open( FILE, $codefile ) ) || &FatalError( 1006, $codefile );
# Load file contents while(<FILE>) { next if (/^;/ || /^\s+$/); @items = split( /\s+/, $_ ); ( @items > 5 ) || &FatalError( 1912, $CODESFNAME ); (exists $CODES{uc$items[0]})? &FatalError( 1911, $CODESFNAME ): (@{$CODES{uc$items[0]}}=@items[1..$#items]); } close( FILE );
for $code (keys %CODES) { &SetMacro( $VfyPath{'Exclude_Subdir'}, &GetMacro($VfyPath{'Exclude_Subdir'}) . "; " . $ENV{_NTTREE} . "\\" . lc$code . "; " . $ENV{_NTTREE} . "\\" . substr(lc$CODES{$code}->[4],1), 0 ); } return;
} # LoadCodesFile
# IsLangOK # Verifies if the current language matches the value of the Langs field. # Input # Langs field value # Output # 1 if the current language matches Langs, 0 otherwise # Usage # IsLangOK( "FE", "jpn" );
sub IsLangOK { return cklang::CkLang($_[1], $_[0]); } # IsLangOK
# VerifyCover # Verify the @{T$MAPPING} is covered the whole tree # Input & Output # none # Usage # &VerifyCover();
sub VerifyCover { my(%tree)=();
return if (&GetMacro($VfyPath{'Exclude_Root'}) eq "");
SetMacro( $VfyPath{'Exclude_Subdir'}, &Undefine_Exclude_Lang_Subdir(&GetMacro($VfyPath{'Exclude_Subdir'})), 0 );
# Create except tree from @T$Mapping array, then remove the dummy to find all branch into $tree_hashptr &Create_Tree_Branch(\%tree);
# Remove Root and its parents from branch tree &Remove_Root_List(\%tree, grep {/\S/} split(/\s*;\s*/, &GetMacro($VfyPath{'Exclude_Root'})));
# Find unmapping folders from branch tree, also remove the empty folders and the specified file-pattern files (such as slm.ini). &Find_UnMapping(\%tree); } # VerifyCover
# Undefine_Exclude_Lang_Subdir # Include $ENV{_NTTREE}\\$lang and $ENV{_NTTREE}\\$class for cover verification # Input # $str - The string list of exclude subdir # Output # $str - The string list after remove $ENV{_NTTREE}\\$lang and $ENV{_NTTREE}\\$class # Usage # &Undefine_Exclude_Lang_Subdir(&GetMacro($VfyPath{'Exclude_Subdir'}));
sub Undefine_Exclude_Lang_Subdir { my $str=shift; my $lang=&GetMacro( "language" ); my $pat1=&GetMacro( "_NTTREE" ) . "\\$lang;"; my $pat2=&GetMacro( "_NTTREE" ) . "\\$CODES{$lang}->[4];"; $str=~s/\Q$pat1\E//i; $str=~s/\Q$pat2\E//i;
return $str; }
# Create_Tree_Branch # Create except tree from @T$Mapping array, then remove the dummy to find all branch into $tree_hashptr # Input # $tree_hashptr - a hash pointer for store the branch tree # # Output # none - the branch tree stored in %$tree_hashptr # Usage # &Create_Tree_Branch(\%mytree);
sub Create_Tree_Branch { my($tree_hashptr)=@_; my(%except)=();
# Create Exception Tree from @T$Mapping array &Get_Except(\%except);
# Remove the subdir if parent defined, the remains create into hash (parent dir) - hash (current dir) &Remove_Dummy_Create_Branch(\%except,$tree_hashptr); }
# Get_Except # Create Exception Tree from @T$Mapping array # Input # $except_hashptr - a hash pointer for store the exception tree # # Output # none - the exception tree stored in %$except_hashptr # # Usage # &Get_Except(\%myexcept);
sub Get_Except { my($except_hashptr)=@_;
my($tablename, $hashptr)=();
## Predefine except directories for (split(/\s*;\s*/, &GetMacro($VfyPath{'Exclude_Subdir'}))) { next if ($_ eq ""); if (/\.\.\./) { map({$$except_hashptr{$_}=1} &Find_Dot_Dot_Dot(lc$_)); } else { $$except_hashptr{lc $_}=1; } }
## According bindir or tokdir to define the sourcepath to %except for $tablename (@MAPPINGS) { for $hashptr (@{"T$tablename"}) { if (exists $$hashptr{'R'}) { if ($$hashptr{'R'} eq 'n') { $$except_hashptr{ lc$$hashptr{SrcPath} . "\\."} = 1 } else { $$except_hashptr{lc$$hashptr{SrcPath}} = 1 } } elsif (exists $$hashptr{SrcPath}) { if (defined $$hashptr{SrcFile}) { $$except_hashptr{lc($$hashptr{SrcPath} . "\\" . $$hashptr{SrcFile})} = 1 } else { $$except_hashptr{lc$$hashptr{SrcPath}} = 1 } } elsif (exists $$hashptr{TokPath}) { if (defined $$hashptr{TokFile}) { $$except_hashptr{lc($$hashptr{TokPath} . "\\" . $$hashptr{TokFile})} = 1 } else { $$except_hashptr{lc$$hashptr{TokPath}} = 1 } } else {&Error( 2051, $hashptr);} } } }
# Find_Dot_Dot_Dot # Collect the combination of the ... folders # Input # $path - A path contains "..." # # Output # keys %regist - The array of all the combination found in the path # # Usage # &Find_Dot_Dot_Dot("$ENV{_NTTREE}\\...\\symbols.pri");
sub Find_Dot_Dot_Dot { my ($path) = @_; my ($mymatch, $file, %regist); my @piece=split(/\\\.{3}/, $path);
for $file ( `dir /s/b/l $piece[0]$piece[-1] 2>nul`) { chomp $file; $mymatch = &match_subjects($file, \@piece); next if (!defined $mymatch); $regist{$mymatch}=1; } return keys %regist; }
# match_subjects # The subroutine called by Find_Dot_Dot_Dot, which perform the match for # all the pieces of the infomation of the path with the file. For example # # Input # $file - The fullpath filename # $pieceptr - The array ptr # # Output # undef if not match # $match - store the path it matched # # Usage # &match_subjects( # "D:\\ntb.binaries.x86fre\\dx8\\symbols\\retail\\dll\\migrate.pdb", # \@{"D:\\ntb.binaries.x86fre", "symbols\\", "dll\\"} ); # Return => "D:\\ntb.binaries.x86fre\\dx8\\symbols\\retail\\dll" #
sub match_subjects { my ($file, $pieceptr)=@_; my $match;
for (@$pieceptr) { return if ($file!~/\Q$_\E/); $match .= $` . $&; $file = $'; } return $match; }
# Remove_Dummy_Create_Branch # Remove the subdir if parent defined, the remains create into hash (parent dir) - hash (current dir) # Input # $except_hashptr - stored the exception hash from %T$Mapping # $tree_hashptr - will store the covered tree in hash - hash # # Output # none - the covered tree stored in %$tree_hashptr # # Usage # &Remove_Dummy_Create_Branch(\%myexcept, \%mybranch);
sub Remove_Dummy_Create_Branch { my($except_hashptr,$tree_hashptr)=@_;
next1: for (keys %$except_hashptr) {
# loop all its parent while(/\\+([^\\]+)/g) {
# Is this folder covered by its parent? if (exists $$except_hashptr{$`}) { # Remove current folder delete $$except_hashptr{$_}; next next1; } else { # define the $tree{$parent}->{$child}=1 $$tree_hashptr{$`}->{$&}=1; }
} } }
# Remove_Root_List # Remove Root and its parents from branch tree # Input # $tree_hashptr - a hash pointer, %$tree_hashptr is the branch tree # @root_list_ptr - a array for the root list # # Output # none - the root and its parent will be remove from branch tree (%$tree_hashptr) # # Usage # &VerifyCover();
sub Remove_Root_List { my($tree_hashptr, @root_list_ptr)=@_;
my $concern;
# split the $root_list by ';' for (@root_list_ptr) {
# loop all its parents while(/\\/g) { # remove all the parent folder delete $$tree_hashptr{$`} if ($` ne $_); } } Next2: for (keys %$tree_hashptr) { for $concern (@root_list_ptr) { next Next2 if (/\Q$concern\E/i); } delete $$tree_hashptr{$_}; } }
# Find_UnMapping # Find unmapping folders from branch tree, also remove the empty folders and the specified file-pattern files (such as slm.ini). # Input # $tree_hashptr - a hash - hash pointer, %$tree_hashptr is the branch tree # # Output # none # Usage # &VerifyCover();
sub Find_UnMapping { my($tree_hashptr)=@_;
my ($parent_dir, $mykey, @myfiles);
# for each tree for $parent_dir (keys %{$tree_hashptr}) { # find its children for (`dir /b/ad/l $parent_dir 2>nul`) { chomp; # only concern not exist in covered tree if (!exists $$tree_hashptr{$parent_dir}->{"\\$_"}) { $mykey="$parent_dir\\$_"; # find its all children / subidr children, # and remove specified file pattern files, then repro the error for (`dir /s/b/a-d/l $mykey 2>nul`) { if ((!/$VfyPath{'Exclude_File_Pattern'}/i) && (/^(.+)\\[^\\]+$/)) {
### my %comment = ($ENV{"_NTBINDIR"}."\\"."LOC" => " mail localizers to ask why...", $ENV{"_NTTREE"} => " REVIEW map in bldbins.inc, ntloc.inc..." );
my %fixed=();
foreach $dir (keys(%comment)){ $key = $dir; $val = $comment{$dir}; $key=~s/(\w)/\U$1/g; $key=~s/\\/\\\\/g; $key=~s/\./\\./g; $fixed {$key} = $val; }
foreach $dir (keys(%fixed)){ $mykey .= $fixed{$dir} if ($mykey =~/$dir/i); }
##
Error(2052, $mykey); last; } } } } } }
# PopulateReposit # Populates REPOSITORY with data from the mapping tables ( @T variables). # Input & Output <none> # Usage # &PopulateReposit();
sub PopulateReposit {
# Fill filter table &FillFilter();
# Add file entries from TFILES and TBINDIRS &AddFiles();
# If FILTER is defined, verify if all its entries (file names) # were loaded in Repository. for ( keys %FILTER ) {
if ( &IsEmptyHash( \%TARGETS ) || &IsHashKey( \%TARGETS, $_ ) ) {
if ( $FILTER{$_} <= 0 ) { &Error( 1002, sprintf( "%s (%s)", $_, &GetMacro( "FILTER" ) ) ); next; } # if
} # if
} # for
# If TARGETS is defined, verify that all its entries (target names) # were loaded in Repository. for ( keys %TARGETS ) { ( &IsFoundTarget( $_ ) ) || &FatalError( 1004, $_ ); } # for # Fill in info about tokens from TTOKENS and TTOKDIRS &FillTokens();
# Fill in the {Cmd} field for relevant keys &FillCmd();
return;
} # PopulateReposit
# FillCmd # Fill in the {Cmd} REPOSITORY field # Input & Output # none # Usage # &FillCmd();
sub FillCmd {
# Repository key my %key;
# Situations when, for a given (DestPath, DestFile) REPOSITORY key, # the associated NMAKE commands must be stored in REPOSITORY's {Cmd} field: # - clean SYSGEN # - incremental SYSGEN with no command-line targets specified (empty TARGETS)
my $all = $CLEAN || &IsEmptyHash( \%TARGETS );
# Fill in the {Cmd} field print "Translating data to makefile commands...\n";
for $destpath ( sort keys %REPOSITORY ) {
# ( !$all ) || print "\t$destpath"; print "\t$destpath" unless !$all; for $destfile ( sort keys %{$REPOSITORY{$destpath}} ) {
$key{DestPath} = $destpath; $key{DestFile} = $destfile;
# Check special character to avoid nmake broken if ( substr($destfile, -1) eq '$' ) { delete $REPOSITORY{$destpath}->{$destfile}; delete $TARGETS{$destfile}; # &DeleteKey( \%CMD_REPOSITORY, \%key ); &Error(1214, "$destpath\\$destfile"); next; }
# If incremental with targets, print target's full path if ( !$all && &IsHashKey( \%TARGETS, $destfile ) ) {
printf "\t%s\\%s\n", &GetFieldVal( \%REPOSITORY, \%key, "DestPath" ), &GetFieldVal( \%REPOSITORY, \%key, "DestFile" );
} # if
# Translate the current REPOSITORY entry into a set of nmake commands if ( $all || &IsHashKey( \%TARGETS, $destfile ) ) { &RecordToCmd( \%key ); } # if
} # for ( !$all ) || print "\n";
} # for
return;
} # FillCmd
# AddTarget # Add a filename (given on the command line) to the TARGETS table
sub AddTarget {
$TARGETS{lc$_[0]}->{Target}=1;
} # AddTarget
# IsHashKey # Verifies if a given string is key in a given hash # Input # hash (reference) # entry (without path) # Output # 1 if the key exists # 0 otherwise
sub IsHashKey {
return (exists $_[0]->{lc $_[1]});
} # IsHashKey
# IsFoundTarget # Verifies if a filename is marked as found in TARGETS # All targets specified in the command line must be marked in TARGETS at the moment # REPOSITORY is fully loaded. # Input # filename (no path) # Output # 1 if the target is loaded in Repository # 0 otherwise
sub IsFoundTarget {
if ( @{$TARGETS{lc$_[0]}->{NewDestPath}} > 0 ) { return 1; } return 0;
} # IsFoundTarget
# AddTargetPath # Add a DestPath value to one of the NewDestPath, OldDestPath fields of TARGETS # Input # target name # field name (one of NewDestPath and OldDestPath) # field value # Output # none # Usage # &AddTargetPath( "ntdll.dll", "OldDestPath", "f:\\nt\\relbins\\jpn");
sub AddTargetPath {
push @{$TARGETS{lc$_[0]}->{$_[1]}}, lc$_[2];
} # AddTargetPath
# IsEmptyHash # Verifies if a hash table is empty. # Input # hash table (reference) # Output # 1 if hash is empty # 0 otherwise
sub IsEmptyHash { !scalar(%{$_[0]}); # return (scalar(%{$_[0]}) eq 0);
} # IsEmptyHash
# UpdTargetEntries # In case of an incremental build, update the entries corresponding to the TARGETS # from CMD_REPOSITORY according to data from REPOSITORY. # Input & Output # none
sub UpdTargetEntries {
# Target name my $tname;
# Indexes my $i;
# CMD_REPOSITORY key my %key;
# OldDestPath array (reference) my $destref;
if ( $CLEAN || &IsEmptyHash(\%TARGETS) ) { return; }
for $tname ( keys %TARGETS ) {
$key{DestFile} = $tname;
# Delete CMD_REPOSITORY entries with DestFile equals to target name $destref = \@{$TARGETS{$tname}->{OldDestPath}};
for ( $i=0; $i < @{$destref}; $i++ ) { $key{DestPath} = $destref->[$i]; &DeleteKey( \%CMD_REPOSITORY, \%key );
} # for
# Copy data for TARGETS from REPOSITORY to CMD_REPOSITORY $destref = \@{$TARGETS{$tname}->{NewDestPath}};
for ( $i=0; $i < @{$destref}; $i++ ) {
$key{DestPath} = $destref->[$i];
# Use the same Cmd for CMD_REPOSITORY as for REPOSITORY &SetField( \%CMD_REPOSITORY, \%key, "DestPath", &GetFieldVal( \%REPOSITORY, \%key, "DestPath" ) ); &SetField( \%CMD_REPOSITORY, \%key, "DestFile", &GetFieldVal( \%REPOSITORY, \%key, "DestFile" ) ); &SetField( \%CMD_REPOSITORY, \%key, "Cmd", &GetFieldVal( \%REPOSITORY, \%key, "Cmd" ) );
} # for
} # for
return;
} # UpdTargetEntries
# AddFiles # Parses TFILES and TBINDIRS and adds entries to REPOSITORY. # Input & Output <none> # Usage # &AddFiles();
sub AddFiles {
# Current element of TBINDIRS ( reference ) my $recref;
# Current directory contents my @files;
# Indexes my($i, $j, $dir);
print "Adding file data...\n";
# Add the files listed in TFILES to REPOSITORY for ( $i=0; $i < @TFILES; $i++ ) {
# Call AddEntry with 1, meaning that the existence of the # file is queried and a non fatal error fired if the file is missing. # As the file is explicitly listed in FILES MAP, it is natural to suppose # it should exist. &AddEntry( $TFILES[$i], 1 );
} #for
@FILES = ();
# Add the files found in the directories stored by TBINDIRS for ( $i=0; $i < @TBINDIRS; $i++ ) {
$recref = $TBINDIRS[$i];
# Load all files from the SrcPath directory @files = (); @files = `dir /a-d-h /b $recref->{SrcPath} 2\>nul`;
print "\t$recref->{SrcPath}\n";
# Add one entry in REPOSITORY for each file found at SrcPath for ( $j=0; $j < @files; $j++ ) {
chop $files[$j]; # $_.='$' if (substr($_,-1) eq '$');
# Found that in NT5, the result of dir /b starts with an empty line if ( "$files[$j]" eq "" ) { next; }
$recref->{SrcFile} = $files[$j]; $recref->{DestFile} = $recref->{SrcFile};
# Call AddEntry with 0, to inhibit the file existence checking, # as SrcFile is a result of a dir command (see above). &AddEntry( $recref, 0);
} # for
} # for
@TBINDIRS = ();
return;
} # AddFiles
# AddEntry # Adds a new entry to REPOSITORY, only if REPOSITORY does not already have an # entry with the same (DestPath, DestFile) key. # Input # new record (reference) # boolean argument, saying if the file existence should be checked # Output # none # Usage # &AddEntry( $recref);
sub AddEntry {
my($recref, $e_check) = @_;
# Db key my %key;
# Add entry to Repository based on file existence and on # the type of the call (clean, incremental, with or without targets) $recref->{DestFile} !~/\s/ or &Error( 4001, "\"$recref->{SrcPath}\\$recref->{SrcFile}\"") and return;
if ( ( &IsEmptyHash( \%FILTER ) || # no filters &IsHashKey( \%FILTER, $recref->{DestFile} ) ) # filter file && ( $CLEAN || # clean sysgen &IsEmptyHash( \%TARGETS ) || # incremental with no targets specified &IsHashKey( \%TARGETS, $recref->{DestFile} ) ) # incremental with targets, and the current # entry is one of the targets ) {
if ( $e_check ) { if ( ! -e "$recref->{SrcPath}\\$recref->{SrcFile}" ) { &Error( 2101, "$recref->{SrcPath}\\$recref->{SrcFile}" ); return; } # if } # if
# Set the key $key{DestPath} = $recref->{DestPath}; $key{DestFile} = $recref->{DestFile};
&AddFileInfo( $recref, \%key );
} # if
return;
} # AddEntry
# AddFileInfo # Input # record to be added # Output # <none> # Usage # &AddFileInfo( $recref, \%key );
sub AddFileInfo {
my($recref, $keyref) = @_;
# Nothing to do if the DestFile already has an entry in the Repository ( ! &IsRepositKey( \%REPOSITORY, $keyref ) ) || return;
if ( &IsHashKey( \%TARGETS, $keyref->{DestFile} ) ) { &AddTargetPath( $keyref->{DestFile}, "NewDestPath", $keyref->{DestPath} ); } # if
if ( &IsHashKey( \%FILTER, $recref->{DestFile} ) ) { $FILTER{lc( $recref->{DestFile} )}++; } # if
# Set the fields of the new entry &SetField( \%REPOSITORY, $keyref, "DestPath", $recref->{DestPath} ); &SetField( \%REPOSITORY, $keyref, "DestFile", $recref->{DestFile} ); &SetField( \%REPOSITORY, $keyref, "SrcPath", $recref->{SrcPath} ); &SetField( \%REPOSITORY, $keyref, "SrcFile", $recref->{SrcFile} ); &SetField( \%REPOSITORY, $keyref, "SrcDbg", $recref->{SrcDbg} ); &SetField( \%REPOSITORY, $keyref, "DestDbg", $recref->{DestDbg} );
SWITCH: { if ( $recref->{Xcopy} eq "y" ) {
&SetField( \%REPOSITORY, $keyref, "OpType", "1" ); last SWITCH; } # case
# default ## &SetField( \%REPOSITORY, $keyref, "OpType", "0" ); } # SWITCH
return;
} # AddFileInfo
# IsRepositKey # Looks in REPOSITORY for the given key. # Used to decide if a record will be added or not to REPOSITORY. # Input # map (reference) # the key (reference) # Output # 1 if the key is found and 0 otherwise # Usage # $is_key_found = &IsRepositKey( \%REPOSITORY, $keyref );
sub IsRepositKey {
# my( $mapref, $keyref ) = @_;
# The key referred by keyref is modified, # but this saves a lot of execution time # so we prefer not to work with a copy of the key
return ( exists $_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}} );
} # IsRepositKey
# DeleteKey # Input # map name (one of REPOSITORY, CMD_REPOSITORY) (reference) # key (reference) # Output # none
sub DeleteKey {
# my( $mapref, $keyref ) = @_;
# The key referred by keyref is modified, # but this saves a lot of execution time # so we prefer not to work with a copy of the key
delete $_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}};
} # DeleteKey
# FillTokens # Adds info regarding tokens in REPOSITORY (TTOKENS and TTOKDIRS). # Input & Output <none> # Usage # &FillTokens();
sub FillTokens {
# Current entry from TTOKDIRS my $recref;
# List of files found in the current directory my @files;
# Indexes my( $i, $j, $k );
# Custom resource file name my $custfile;
# Custom reosurces array my @custres;
# Custom reosurces hash for verification my %custres_h;
# Temporary store the file hash for special sort @files my %hashfiles;
print "Filling token data...\n";
# Fill in TTOKENS for ( $i=0; $i < @TTOKENS; $i++ ) {
$recref = $TTOKENS[$i];
# rsrc if ( &IsRsrcTok( $recref->{TokFile} ) ) {
&FillTokEntry( $recref, 1 ); next;
} # if
# bingen if ( &IsBgnTok( $recref->{TokFile} ) ) {
# Identify all the custom resources under the following assumptions: # * the token and the custom resources of a binary are in the same directory # * for a "<binary_name>.<token_extension>" token, a custom resource has # the name <binary_name>_<binary_extension>_<custom_resource_name> # * a token is always followed in lexicographic order by its custom resources # or by another token
# Parse the token file to find embeded files # open(F, "$recref->{TokPath}\\$recref->{TokFile}"); # my @text=<F>; # close(F); # for $str (@text) { # if (($str=~/\[[^\]]+\|1\|0\|4\|\"[\w\.]*\"]+\=(\w+\.\w+)\s*$/)&&(-e $1)) { # $custres_h{$1}=0; # } # } # undef @text;
# Store it to an array for later use # @custres=keys %custres_h; # Verify all its names' token files are included
$custfile = $recref->{DestFile}; $custfile =~ s/\./_/g;
@custres=`dir /a-d-h /b /on $recref->{TokPath}\\$custfile\* 2\>nul`;
# map({$custres_h{$_}++} `dir /a-d-h /b /on $recref->{TokPath}\\$custfile\* 2\>nul`); # map({&Error(2020, $_) if ($custres_h{$_} eq 0)} keys %custres_h);
$recref->{CustRc} = ""; for ( $j=0; $j < @custres; $j++ ) { chop $custres[$j]; # $_.='$' if (substr($_,-1) eq '$');
# Found that in NT5, the result of dir /b starts with an empty line if ( $custres[$j] eq "" ) { next; } # if
$recref->{CustRc} .= " \\\n\t\t\t$recref->{TokPath}\\$custres[$j]";
} # for
# Call FillTokEntry with 1, meaning that the existence of the # token file is queried and a non fatal error fired if the file # is missing. # As the token is explicitly listed in TOKENS MAP, it is natural # to suppose it should exist. &FillTokEntry( $recref, 1 );
next;
} # if
# default &Error( 2013, "$recref->{TokPath}\\$recref->{TokFile}" );
} #for
@TTOKENS = ();
# Fill in TTOKDIRS for ( $i=0; $i < @TTOKDIRS; $i++ ) {
$recref = $TTOKDIRS[$i];
print "\t$recref->{TokPath}\n";
# Load all files from the TokPath directory # @files = ();
%hashfiles=map({&fullnamevalue()} `dir /a-d-h /l /b $recref->{TokPath} 2\>nul`);
@files=sort {$hashfiles{$a} cmp $hashfiles{$b}} keys %hashfiles; # because in the same folder, so we could ignore \a\b.txt, \a\b\c.txt, \a\c.txt problem.
# Fill in the corresponding entry from REPOSITORY for each token found at TokPath for ( $j=0; $j < @files; $j++ ) {
# Found that in NT5, the result of dir /b starts with an empty line (bug) if ( $files[$j] eq "" ) { next; }
# bingen if ( &IsBgnTok( $files[$j] ) ) {
$recref->{TokFile} = $files[$j]; $recref->{DestFile} = &TokToFile( $recref->{TokFile} );
# Verify if the next entry in @files is an rsrc token for the same filename if ( $j < @files-1 && &IsRsrcTok( $files[$j+1] ) ) { if ( &TokToFile( $files[$j]) eq &TokToFile( $files[$j+1] ) ) {
&Error( 2018, "$recref->{DestPath}\\$files[$j] and $recref->{DestPath}\\$files[$j+1]"); $j = $j+1; } # if } # if
# Identify file's custom resources $custfile = $recref->{DestFile}; $custfile =~ s/\./_/g;
$recref->{CustRc} = ""; for ( $k = $j+1; $k < @files; $k++ ) {
if ( &IsCustRes( $files[$k], $custfile ) ) { $recref->{CustRc} .= " \\\n\t\t\t$recref->{TokPath}\\$files[$k]"; } # if else { last; } # else
} # for $j = $k-1;
# Call FillTokEntry with 0, to inhibit the file existence checking, # as TokFile is a result of a dir command (see above).
&FillTokEntry( $recref, 0, \@custres );
next; } # if else { # rsrc if ( &IsRsrcTok( $files[$j] ) ) {
$recref->{TokFile} = $files[$j]; $recref->{DestFile} = &TokToFile($recref->{TokFile});
# look for bogus custom resources $custfile = $recref->{DestFile}; $custfile =~ s/\./_/g;
for ( $k = $j+1; $k < @files; $k++ ) {
if ( &IsCustRes( $files[$k], $custfile ) ) {
&Error( 2019, "$recref->{TokPath}\\$files[$k] for $recref->{TokPath}\\$files[$j]" ); } # if else { last; } # else
} # for $j = $k-1;
&FillTokEntry( $recref, 0, \@custres );
next; } # if else { # default &Error( 2013, "$recref->{TokPath}\\$files[$j]" );
} # else
} #else
} # for
} # for
@TTOKDIRS = ();
} # FillTokens
sub fullnamevalue { chop;
# $_.='$' if (substr($_,-1) eq '$'); /^([^\.\_]+)/o;
my($value, $key)=($1, $_);
while (/([\.\_])([^\.\_]*)/go) { $value .= (exists $MAP_BINGEN_TOK_EXT{".$2"})?"_" . $MAP_BINGEN_TOK_EXT{".$2"} : "_$2"; }
return ($key, $value); }
# FillTokEntry # Given a REPOSITORY key, adds info on tokens (@TTOKENS and @TTOKDIRS). # Input # a token record # boolean argument, saying if the existence of the token needs to be verified # Output # <none>
sub FillTokEntry {
my($recref, $e_check) = @_;
# %key = is a hash table (as defined by the database key) my %key;
# Token information is added to an existing Repository entry # based on token file existance and the type of the sysgen call # (clean, incremental with or without targets)
if ( ( &IsEmptyHash( \%FILTER ) || # no filter &IsHashKey( \%FILTER, $recref->{DestFile} ) ) # filter file && ( $CLEAN || # clean sysgen &IsEmptyHash( \%TARGETS ) || # incremental without targets &IsHashKey( \%TARGETS, $recref->{DestFile} ) ) # incremental with targets and the current # entry corresponds to a target
) {
if ( $e_check ) { if ( ! -e "$recref->{TokPath}\\$recref->{TokFile}" ) { &Error( 2102, "$recref->{TokPath}\\$recref->{TokFile}" ); return; } # if } # if
# Set REPOSITORY key $key{DestPath} = $recref->{DestPath}; $key{DestFile} = $recref->{DestFile};
# Fill in the information needed in REPOSITORY &FillTokInfo( $recref, \%key );
} # if
return;
} # FillTokEntry
# FillTokInfo # Adds information for bingen switches to an existing REPOSITORY entry. # Sets the following keys ( fields ): TokPath, TokFile, BgnSw, OpType, ITokPath. # Input # hash table with the @FTOKENS keys, representing one token record (reference) # key to identify the REPOSITORY entry (reference) # Output # <none> # Usage # &FillTokInfo( $recref, \%key );
sub FillTokInfo {
my( $recref, $keyref ) = @_;
# Operation type my $optype;
# A token record does not create new entries, just adds/updates fields of an existing entry. if ( ! &ExistsBinForTok( $keyref, $recref ) ) { my $notfound = 1; my $key = lc($recref->{"TokPath"}."\\".$recref->{"TokFile"}); my $hotfix = \%hotfix; foreach my $fix (keys(%hotfix)){ my $depend = $hotfix->{$fix}->{"depend"}; foreach $dep (@$depend){ if ($key eq $dep) { print STDERR "found hotfix for $key\n" if $DEBUG; $notfound = 0; } } } if ($notfound){ &Error( 2011, "$recref->{TokPath}\\$recref->{TokFile}" ); return; } } # if
# If no key found in REPOSITORY for this entry, then return. # ( the token does not follow the default rule given by the TOKDIRS map; # it might be associated with another binary via the TOKENS map )
if ( ! &IsRepositKey( \%REPOSITORY, $keyref ) ) { return; }
# Verify if the binary is locked (XCopy = y) for localization. # If locked, the binary will not be localized via bingen or rsrc and an xcopy command # will apply instead, even though a token exists in the token tree.
$optype = &GetFieldVal( \%REPOSITORY, $keyref, "OpType" );
SWITCH: { if ( $optype eq "1" ) {
&Error( 2014, sprintf( "%s\\%s for %s\\%s", $recref->{TokPath}, $recref->{TokFile}, &GetFieldVal( \%REPOSITORY, $keyref, "SrcPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile" ) ) ); return; } # if
if ( $optype eq "2" ) { my($previous, $current)=(&GetFieldVal( \%REPOSITORY, $keyref, "TokPath" ) . "\\" . &GetFieldVal( \%REPOSITORY, $keyref, "TokFile" ), $recref->{TokPath} . "\\" . $recref->{TokFile});
&Error( 2012, sprintf( "%s and %s", $previous, $current)) if ($previous ne $current);
return; } # if
if ( $optype eq "-" ) { # Set the token-related fields for the binary given by the key &SetField( \%REPOSITORY, $keyref, "TokPath", $recref->{TokPath} ); &SetField( \%REPOSITORY, $keyref, "TokFile", $recref->{TokFile} ); &SetField( \%REPOSITORY, $keyref, "OpType", "2" ); &SetField( \%REPOSITORY, $keyref, "ITokPath", $recref->{ITokPath} ); &SetField( \%REPOSITORY, $keyref, "BgnSw", $recref->{BgnSw} ); &SetField( \%REPOSITORY, $keyref, "LocOp", $recref->{LocOp} ); &SetField( \%REPOSITORY, $keyref, "CustRc", $recref->{CustRc} );
last SWITCH; } # if
# default &FatalError( 3011, sprintf( "$recref->{DestPath}\\$recref->{DestFile} %s", &GetFieldVal( \%REPOSITORY, $keyref, "OpType") ) );
} # SWITCH
return;
} # FillTokInfo
# ExistsBinForTok # Looks in Repository for an entry corresponding to a given token # Input # a token record ( reference ) # a Repository key ( reference ) # Output # 1 - entry found ( token info might be already completed ) # 0 - otherwise # Example # if ( &ExistsBinForTok( $recref ) ) { ... }
sub ExistsBinForTok {
my( $keyref, $recref ) = @_;
# Copy key my %tmpkey;
# Tok info ( Repository ) my( $tokpath, $tokfile );
if ( &IsRepositKey( \%REPOSITORY, $keyref ) ) { return 1; }
$tmpkey{DestFile} = $keyref->{DestFile};
for ( keys %REPOSITORY ) {
$tmpkey{DestPath} = $_;
if ( &IsRepositKey( \%REPOSITORY, \%tmpkey ) ) {
$tokpath = &GetFieldVal( \%REPOSITORY, \%tmpkey, "TokPath" ); $tokfile = &GetFieldVal( \%REPOSITORY, \%tmpkey, "TokFile" );
if ( lc($tokfile) eq lc($recref->{TokFile}) && lc($tokpath) eq lc($recref->{TokPath}) ) { return 1; } # if
} # if
} # for
return 0;
} # ExistsBinForTok
# TokToFile # Converts a given bingen token name to a binary name. # Input # token name # Output # binary name # Usage # $fname = &TokToFile( $tokname );
sub TokToFile {
my( $tokname ) = @_;
# Output my $imagename;
# Token extension name my $tokext;
chomp $tokname;
$imagename = $tokname;
# rsrc token if ( &IsRsrcTok( $tokname ) ) { $imagename =~ s/\.rsrc//; return $imagename; } # if
# bingen token if ( &IsBgnTok( $tokname ) ) {
$tokext = $tokname; $tokext =~ /(\.\w+_)/; $tokext = $1;
if ( not exists $MAP_BINGEN_TOK_EXT{lc($tokext)} ) { &Error( 2017, $tokname); return; } # if
$imagename =~ s/$tokext/$MAP_BINGEN_TOK_EXT{lc($tokext)}/i;
return $imagename; } # if
# default &Error( 2013, $tokname );
} # TokToFile
sub IsRsrcTok { ($_[0]=~/\.rsrc\r?$/i)|0; } # IsRsrcTok
sub IsRsrcOp { &IsRsrcTok( shift ); } # IsRsrcOp
sub IsBgnTok { ($_[0]=~/_\r?$/)|0; } # IsBgnTok
sub IsBgnOp { &IsBgnTok(shift); } # IsBgnOp
sub IsCustRes { my $name = shift; $name=~ s/\./_/g; ($name=~/$_[0]/)|0; } # IsCustRes
# SetField # Updates a key (field) of a given REPOSITORY entry with a given value. # Input # map (reference) # key value (reference) # field name # field value # Output # none # Usage # &SetField( \%key, "TokPath", $TokPath );
sub SetField {
# my( $mapref, $keyref, $fname, $fval ) = @_;
# The key referred by keyref is modified, # but this saves a lot of execution time # so we prefer not to work with a copy of the key
# $mapref->{$keyref->{DestPath}}->{$keyref->{DestFile}}->{$fname} = $fval if ($fval ne '-'); $_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}}->{$_[2]} = $_[3] if ($_[3] ne '-');
return;
} # SetField
# PushFieldVal # Add one element to a field storing an array ({Cmd} field for REPOSITORY) # Input # map (reference) # key value (reference) # field name # field value # Output # none # Usage # &PushFieldVal();
sub PushFieldVal {
# my( $mapref, $keyref, $fname, $fval ) = @_;
# The key referred by keyref is modified, # but this saves a lot of execution time # so we prefer not to work with a copy of the key
push @{$_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}}->{$_[2]}}, $_[3] if ($_[3] ne '-');
} # PushFieldVal
# GetFieldVal # Return the value of a particular key of a given REPOSITORY entry. # Input # map (reference) # key value (reference) # name of the searched field # Output # the value of the searched field # Usage # $srcpath = &GetFieldVal( \%REPOSITORY, \%key, "SrcPath" );
sub GetFieldVal { defined($_[0]->{lc($_[1]->{"DestPath"})}->{lc($_[1]->{"DestFile"})}->{$_[2]})? $_[0]->{lc($_[1]->{"DestPath"})}->{lc($_[1]->{"DestFile"})}->{$_[2]} : "-"; } # GetFieldVal
# GenNmakeFiles # For each entry found in the REPOSITORY, generate the NMAKE description blocks # and write them to a makefile. Also generate a nmake command file with command-line # parameters for nmake. # Input # directory where the nmake files are generated # Output # none # Usage # &GenNmakeFiles(".");
sub GenNmakeFiles {
my( $nmakedir ) = @_;
print "Generating $nmakedir\\makefile...\n";
# Update CMD_REPOSITORY for TARGETS with updated data from REPOSITORY &UpdTargetEntries();
# Identify all targets with description block changed # and keep them in SYSCMD &FillSyscmd();
&GenMakeDir();
# FIX The REPOSITORY changes described in hotfix &FixRepository();
# Write the makefile &WriteToMakefile( "$nmakedir\\makefile", "$nmakedir\\syscmd" );
# Write the syscmd file &WriteToSyscmd( "$nmakedir\\syscmd" );
if ( -e "$nmakedir\\makefile" ) { print "\n$nmakedir\\makefile is ready for execution.\n"; print "Call \"nmake\" to complete aggregation (single thread).\n"; print "Call \"mtnmake sysgen\" to complete aggregation (mutiple threads).\n"; } # if else { print "No MAKEFILE generated. Nothing to do.\n"; } # else
return;
} # GenNmakeFiles
# FillSyscmd # Fills in @SYSCMD global variable with the target names having their description # block changed. It is used for incremental calls only. # Input & Output # none
sub FillSyscmd {
# Repository key my %key;
# Destinationa path, destination file my( $destpath, $destfile );
# Nothing to do in case of: # clean sysgen # incremental sysgen with targets
if ( $CLEAN || !&IsEmptyHash( \%TARGETS ) ) { return; } # if # The call is incremental, without targets, and a previous makefile exists
# It is fine to use REPOSITORY in this case; # the only case when we use CMD_REPOSITORY to write info to the makefile # is an incremental sysgen with targets (when a makefile already exists).
# Compare all description blocks for $destpath ( sort keys %REPOSITORY ) { for $destfile ( sort keys %{$REPOSITORY{$destpath}} ) {
$key{DestPath} = $destpath; $key{DestFile} = $destfile;
# Store in SYSCMD the keys with changed description blocks if ( &CmdCompare( \%key ) ) { push @SYSCMD, "$destpath\\$destfile"; } # if
} # for } # for
return;
} # FillSyscmd
# WriteToMakefile # Generate the makefile # Input # makefile name, syscmd file name # Output # none
sub WriteToMakefile {
my( $makefname, $sysfname ) = @_;
# Open the makefile ( open MAKEFILE, ">"."$makefname" ) || &FatalError( 1007, $makefname ); select( MAKEFILE );
# Write "sysgen" target &WriteSysgenTarget($sysfname );
# Write "all" target &WriteAllTarget();
# Write file-by-file description blocks &WriteFileCmds();
close(MAKEFILE); select( STDOUT );
return; } # WriteToMakefile
# WriteSysgenTarget # Write "sysgen" target in the generated makefile # It invokes nmake recursively. # Input # filename (including path) of the generated syscmd file # Output # none # Usage # &WriteSysgenTarget("$nmakedir\\syscmd");
sub WriteSysgenTarget {
my($cmdfile) = @_;
printf "sysgen: \n";
# Call nmake @syscmd in the following cases: # sysgen with targets (clean or incremental) # incremental sysgen without targets, but with changes in the description blocks
if ( !&IsEmptyHash( \%TARGETS ) || ( !$CLEAN && ( @SYSCMD > 0 ) ) ) { printf "\t\@nmake \@$cmdfile /K /NOLOGO\n"; } # if
# Call nmake all in the following cases: # sysgen without targets (clean or incremental)
if ( &IsEmptyHash( \%TARGETS ) ) { if ( $CLEAN ) { printf "\t\@nmake /A all /K /NOLOGO\n"; } # if else { printf "\t\@nmake all /K /NOLOGO\n"; } # else } # if
printf "\n";
return;
} # WriteSysgenTarget
# WriteAllTarget # Writes "all"'s target dependency line in the makefile. # Input & Output # none # Usage # &WriteAllTarget();
sub WriteAllTarget {
# Table (reference): REPOSITORY or CMD_REPOSITORY my $mapref;
# Destination path, destination file my( $destpath, $destfile );
my $chars;
my $steps;
my @alltargets;
my $i;
my $j;
my $k;
my $total=0;
# Use data from CMD_REPOSITORY or REPOSITORY, # depending if SYSGEN is called or not incrementally, # with or without targets.
$mapref = \%CMD_REPOSITORY; if ( $CLEAN || &IsEmptyHash( \%TARGETS ) ) { $mapref = \%REPOSITORY; } # if
# MAKEFILE file handler is the default. print "all : \\\n\t" ;
$i = 0;
@alltargets = map({$total+=scalar(keys %{$mapref->{$_}});$_;} sort keys %{$mapref});
if ($alltargets[$i]=~/\\\\\\makedir/) { print "\\\\\\Makedir\\Makedir "; $i++; }
$SECTIONS = $DEFAULT_SECTIONS if ($SECTIONS!~/\d+/);
$SECTIONS = 1 if ($#alltargets < $SECTIONS);
$chars=length($SECTIONS); $steps=$total / $SECTIONS;
for ($j = 1; $j <= $SECTIONS; $j++) { printf("${SECTIONNAME}\%0${chars}d\%0${chars}d ", $SECTIONS, $j); }
for ($j = 0, $k = 0;$i <= $#alltargets; $i++) {
for $destfile ( sort keys %{$mapref->{$alltargets[$i]}} ) {
if ($j <= $k++) {
$j += $steps; printf("\n\n${SECTIONNAME}\%0${chars}d\%0${chars}d : ", $SECTIONS, $j / $steps);
}
$key{DestPath} = $alltargets[$i]; $key{DestFile} = $destfile; my $quot_ = $destfile =~ /\s/ ? "\"":""; printf( " \\\n\t${quot_}%s\\%s${quot_}", &GetFieldVal( $mapref, \%key, "DestPath" ), &GetFieldVal( $mapref, \%key, "DestFile" ) );
} # for
} # for
print " \n\n";
return;
} # WriteAllTarget
# WriteFileCmds # For every file, write its dependency block in the makefile. # Input & Output # none # Usage # &WriteFileCmds();
sub WriteFileCmds {
# Table (reference): REPOSITORY or CMD_REPOSITORY my $mapref;
# Counter my $i;
# Reference to the Cmd field my $cmdref;
# Destinationa path, destination file my( $destpath, $destfile );
# Use data from CMD_REPOSITORY or REPOSITORY, # depending if SYSGEN is called or not incrementally, # with or without targets.
$mapref = \%CMD_REPOSITORY; if ( $CLEAN || &IsEmptyHash( \%TARGETS ) ) { $mapref = \%REPOSITORY; } # if
# Write file-by-file description blocks for $destpath ( sort keys %{$mapref} ) { for $destfile ( sort keys %{$mapref->{$destpath}} ) {
# Print {Cmd} field $cmdref = \@{$mapref->{$destpath}->{$destfile}->{Cmd}}; for ( $i=0; $i < @{$cmdref}; $i++ ) { print $cmdref->[$i]; } # for print "\n";
} # for } # for
return; } # WriteFileCmds
# CmdCompare # Given a key, compare the {Cmd} field from REPOSITORY to the {Cmd} CMD_REPOSITORY field. # Input # repository type key (reference) # Output # 0 if commands compare OK # 1 if commands are different # Usage # $is_different = &CmdCompare( \%key );
sub CmdCompare {
my( $keyref ) = @_;
# Cmd fields my( $repref, $cmdref );
# Indexes my $i;
$repref = &GetFieldVal( \%REPOSITORY, $keyref, "Cmd" ); $cmdref = &GetFieldVal( \%CMD_REPOSITORY, $keyref, "Cmd" );
if ( scalar( @{$repref} ) != scalar( @{$cmdref} ) ) { return 1; }
for ( $i = 0; $i < @{$repref}; $i++ ) { if ( lc( $repref->[$i] ) ne lc( $cmdref->[$i] ) ) { return 1; } } # for
return 0;
} # CmdCompare
# RecordToCmd # Converts one entry from REPOSITORY to a set of cmd instructions, # stored in the REPOSITORY as well # Input # key identifying the REPOSITORY entry (reference) # Output # none # Usage # &RecordToCmd( $keyref );
sub RecordToCmd {
#Input my( $keyref ) = @_;
# Build the command array according to the operation type (xcopy or bingen) SWITCH: { if ( &IsXcopyCmd( $keyref ) ) { &GenXcopy( $keyref ); last SWITCH; } # if
if ( &IsLocCmd( $keyref ) ) { &GenLocCmd( $keyref ); last SWITCH; } # if
# default &FatalError( 3011, sprintf "$keyref->{DestPath}\\$keyref->{DestFile} %s", &GetFieldVal( \%REPOSITORY, $keyref, "OpType") ); } # SWITCH
# Now generate the muibld command line
# No more processing if the entry is not anymore in Repository # (for ex. because DeleteKey was called by GenBgnCmd) if ( ! &IsRepositKey( \%REPOSITORY, $keyref ) ) { return; } # if
if ( ! &GetMacro( "ntdebug" ) && # only retail builds &GetMacro( "GENERATE_MUI_DLLS" ) # and only when requested ) { &GenMUICmd( $keyref ); } # if
} # RecordToCmd
# IsXcopyCmd # Verifies if one entry from REPOSITORY corresponds to an xcopy command. # Input # key identifying the REPOSITORY entry (reference) # Output # 1 for xcopy, 0 otherwise # Usage # $bxcopy = &IsXcopyCmd( $keyref );
sub IsXcopyCmd {
my( $keyref ) = @_;
# Output $bxcopy = 0;
# Operation Type my $optype;
# Look for the value of OpType # "0" or "1" indicates that an xcopy command corresponds to this record
$optype = &GetFieldVal( \%REPOSITORY, $keyref, "OpType" );
SWITCH: { if ( $optype eq "-" ) { $bxcopy = 1; last SWITCH; } #if
if ( $optype eq "1" ) { $bxcopy = 1; last SWITCH; } #if
# default # nothing to do
} # SWITCH
return $bxcopy;
} # IsXcopyCmd
# IsLocCmd # Verifies if one entry from REPOSITORY corresponds to a bingen command. # Input # key identifying the REPOSITORY entry (reference) # Output # 1 for bingen or rsrc, 0 otherwise # Usage # $bbgn = &IsLocCmd( $keyref );
sub IsLocCmd {
my( $keyref ) = @_;
# Output my $bbgn = 0;
# Operation type my $optype; $optype = &GetFieldVal( \%REPOSITORY, $keyref, "OpType" );
# Look for the value of OpType. # "-a" or "-r" indicates that a localization command (bingen, rsrc, ...) # corresponds to this record.
SWITCH: {
if ( $optype == 2 ) { $bbgn = 1; last SWITCH; } #if
# default # nothing to do
} # SWITCH
return $bbgn;
} # IsLocCmd
sub GetDynData {
my $repository = shift; my $keyref = shift; my $dyndata = $repository->{lc($keyref->{"DestPath"})}->{lc($keyref->{"DestFile"})}; map {$dyndata->{$_} = "-" unless $dyndata->{$_}} keys(%REPOSITORY_TEMPLATE); # cannot use "keys (%$dyndata);"!
$dyndata; }
# GenXcopy # For the given entry from REPOSITORY, generates the xcopy commands. # Input # key identifying the REPOSITORY entry # Output # none # Usage # &GetXcopy( $keyref );
sub GenXcopy {
return if $SYNTAX_CHECK_ONLY;
my( $keyref ) = @_;
# dbgline, pdbline, file line, dependency line my( $dbgline, $pdbline, $symline, $fline, $dline );
# Paths and filenames for the symbols my $dyndata = &GetDynData(\%REPOSITORY, $keyref); my ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) = &NewImageToSymbol($dyndata->{"SrcFile"} , $dyndata->{"DestFile"}, $dyndata->{"SrcPath"} , $dyndata->{"SrcDbg" } );
my( $srcsymfull, $dstsymfull ) = &SetSymPaths( $dyndata->{"SrcDbg"} , $srcext, $srcpdb, $srcdbg, $srcsym, $dyndata->{"DestDbg"}, $dstext );
# Dependency line
$dline = $DLINE; my $quot_ = $dyndata->{"SrcFile"} =~ /\s/ ? "\"" : ""; my $qquot_ = "\\\"" if $quot_;
$dline =~ s/RIGHT/${quot_}$dyndata->{"DestPath"}\\$dyndata->{"DestFile"}${quot_}/; $dline =~ s/LEFT/${quot_}$dyndata->{"SrcPath"}\\$dyndata->{"SrcFile"}${quot_}/;
# Generate the copy commands for the symbols files (dbg, pdb, and sym) $pdbline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $srcsymfull, $srcpdb, $dstsymfull, $dstpdb ); $dbgline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $srcsymfull, $srcdbg, $dstsymfull, $dstdbg ); $symline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $srcsymfull, $srcsym, $dstsymfull, $dstsym );
# Generate binary's xcopy command
$fline = &MakeXcopyCmd( $dyndata->{"SrcFile" } , $dyndata->{"SrcPath" } , $dyndata->{"SrcFile" } , $dyndata->{"DestPath"} , $dyndata->{"DestFile"} , $qquot_ );
# Write the dependency line
&PushFieldVal( \%REPOSITORY, $keyref, "Cmd", "$dline\n" );
$MAKEDIR{$dyndata->{"DestPath"}} = 1;
&PushFieldVal( \%REPOSITORY, $keyref, "Cmd", "\t$fline\n" ); if ( $dbgline || $pdbline || $symline ) { if ( $dstsymfull ne $dyndata->{"DestDbg"}) { $MAKEDIR{$dstsymfull} = 1; } # if } # if if ( $dbgline ) { &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", "\t$dbgline\n" ); } # if if ( $pdbline ) { &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", "\t$pdbline\n" ); } # if if ( $symline ) { &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", "\t$symline\n" ); } # if
return;
} # GenXcopy # SetSymPaths # Set the source and destination paths for the symbols # Can be the same as set in the sysfile (BBT case) # or can be the subdir with the the filename externsion. # The decision is made based on the pdb file existence. # Input # SrcDbg field value # source file extension # source pdb file # source dbg file # source sym file # DestDbg field value # destination file extension # Output # path to the source symbol file # path to the destination symbols file
sub SetSymPaths {
my( $srcpath, $srcext, $srcpdb, $srcdbg, $srcsym, $dstpath, $dstext ) = @_;
# Output my( $srcsymfull, $dstsymfull) = ( $srcpath, $dstpath );
# Verify if the file exists in extension subdir if ( $srcpath ne "-" && $dstpath ne "-" && ( -e "$srcpath\\$srcext\\$srcpdb" || -e "$srcpath\\$srcext\\$srcdbg" || -e "$srcpath\\$srcext\\$srcsym" ) ) { $srcsymfull .= "\\$srcext"; $dstsymfull .= "\\$dstext"; }
return ( $srcsymfull, $dstsymfull );
} # SetSymPaths
# MakeXcopyCmd # Generates an xcopy command for copying a given file. # Input # source path # source file # dest path # dest file # Output # xcopy command # Usage # $xcopy = &MakeXcopyCmd( "f:\\nt\\usa\\\binaries", "advapi.dll", # "f:\\nt\\jpn\\relbins", "advapi.dll" );
sub MakeXcopyCmd {
my( $binary, $srcpath, $srcfile, $dstpath, $dstfile, $qquot_ ) = @_; # last argument : optional my $result; if (( $dstpath eq "-") || ($srcpath eq "-" ) || !(-e "$srcpath\\$srcfile")){ $result = ""; if ($PARSESYMBOLCD && defined($default{$binary})){ my %hints = %{$default{$binary}}; my $ext = $1 if ($dstfile=~/\.(\w+)$/); if ($ext && $hints{$ext}){ my ($sympath, $symname ) = ($hints{$ext}=~/^(.*)\\([^\\]+)$/); my $srchead= &GetMacro("_nttree"); my $dsthead= &GetMacro("dst"); $result = "logerr \"copy /v ${qquot_}$srchead\\$sympath\\$symname${qquot_} ". "${qquot_}$dsthead\\$sympath\\$symname${qquot_}\""; print STDERR " added default for $binary: \"", $ext ,"\"\n" if $DEBUG; } } } else{ $result = "logerr \"copy /v ${qquot_}$srcpath\\$srcfile${qquot_} $dstpath\""; } $result; } # MakeXcopyCmd
# GenLocCmd # For the given entry from REPOSITORY, generates the bingen commands. # Input # key identifying the REPOSITORY entry (reference # array of commands where the output is added (reference) # Output # none # Usage # &GenLocCmd( $keyref );
sub GenLocCmd {
return if $SYNTAX_CHECK_ONLY;
my $keyref = shift; my $op = &GetFieldVal( \%REPOSITORY, $keyref, "TokFile" ); &GenBgnCmd( $keyref ) and return if &IsBgnOp($op); &GenRsrcCmd($keyref) and return if &IsRsrcOp($op);
} # GenLocCmd
sub GenBgnCmd {
my( $keyref ) = @_;
# pdb line, bingen line, dependency line my( $pdbline, $bgnline, $symline, $dline ) = ("", "", "", ""); my $symcmd = "";
# Symbol paths and filenames my ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) = &NewImageToSymbol(&GetFieldVal( \%REPOSITORY, $keyref, "SrcFile"), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile"), &GetFieldVal( \%REPOSITORY, $keyref, "SrcPath"), &GetFieldVal( \%REPOSITORY, $keyref, "SrcDbg" ));
my( $symsrcfull, $symdstfull ) = &SetSymPaths( &GetFieldVal( \%REPOSITORY, $keyref, "SrcDbg" ), $srcext, $srcpdb, $srcdbg, $srcsym, &GetFieldVal( \%REPOSITORY, $keyref, "DestDbg"), $dstext );
$pdbline = &MakeXcopyCmd( &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile"), $symsrcfull, $srcpdb, $symdstfull, $dstpdb); $symline = &MakeXcopyCmd( &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile"), $symsrcfull, $srcsym, $symdstfull, $dstsym); $dbgline = &MakeXcopyCmd( &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile"), $symsrcfull, $srcdbg, $symdstfull, $dstdbg);
# -a | -r | -ai | -ir switch # localization operation type ( append or replace ) my $bgntype = &GetLocOpType( &GetFieldVal( \%REPOSITORY, $keyref, "LocOp" ) );
# -m switch (specific to bingen; different for rsrc) $symcmd = &SetBgnSymSw( $symsrcfull, $srcdbg, $symdstfull, $dstdbg );
# Code page, secondary language id, primary language id (specific for bingen) my( $cpage, $lcid, $prilang, $seclang ) = @{&GetLangCodes( &GetMacro( "language" ) )};
# -p switch (specific for bingen) $cpage = &GetBgnCodePageSw( $cpage );
# -i switch (specific for bingen) my $icodes = &GetBgnICodesSw( $bgntype, &GetMacro( "ILANGUAGE" ) );
# multitoken (bingen) my $multitok = ""; # &GetBgnMultitok( $keyref, $bgntype );
# the unlocalized version of the token must exist as well # if ( substr($bgntype,-2) eq "-a" ) { # if ( ! -e $multitok ) { # &Error( 2015, sprintf( "\n%s\\%s", # &GetFieldVal( \%REPOSITORY, $keyref, "TokPath" ), # &GetFieldVal( \%REPOSITORY, $keyref, "TokFile" ) ) ); # &DeleteKey( \%REPOSITORY, $keyref ); # return; # } # if # } # if
# Sets the bingen command $bgnline = sprintf "logerr \"bingen -n -w -v -f %s %s -o %s %s %s %s %s %s\\%s %s %s\\%s %s\\%s\"", $symcmd, $cpage, $prilang, $seclang, &GetBgnSw( &GetFieldVal( \%REPOSITORY, $keyref, "BgnSw" ) ), $icodes, $bgntype, &GetFieldVal( \%REPOSITORY, $keyref, "SrcPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile" ), $multitok, &GetFieldVal( \%REPOSITORY, $keyref, "TokPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "TokFile" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile" );
# Dependency line $dline = sprintf "%s\\%s : %s\\%s %s\\%s %s%s\n", &GetFieldVal( \%REPOSITORY, $keyref, "DestPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile" ), &GetFieldVal( \%REPOSITORY, $keyref, "SrcPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile" ), &GetFieldVal( \%REPOSITORY, $keyref, "TokPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "TokFile" ), &GetFieldVal( \%REPOSITORY, $keyref, "CustRc" ); $multitok;
&PushFieldVal( \%REPOSITORY, $keyref, "Cmd", "$dline" );
# Dependency line done
# Description block
$MAKEDIR{&GetFieldVal( \%REPOSITORY, $keyref, "DestPath" )} = 1;
if ( $multitok || $pdbline || $symline ) { if ( $symdstfull ne &GetFieldVal( \%REPOSITORY, $keyref, "DestDbg" ) ) { $MAKEDIR{$symdstfull}=1; } # if } # if
&PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$bgnline\n" ) ); if ( $pdbline ) { &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$pdbline\n" ) ); } # if if ( $symline ) { &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$symline\n" ) ); } # if if ( $dbgline && $symcmd!~/\W/) { &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$dbgline\n" ) ); } # if
# Optional resource-only DLL generation
# Description block done
return;
} # GenBgnCmd
sub GenRsrcCmd {
return if $SYNTAX_CHECK_ONLY;
my( $keyref ) = @_;
my $pdbline = ""; # copy pdb line my $dbgline = ""; # copy dbg line my $symline = ""; # copy sym line my $binline = ""; # copy binary line my $rsrcline= ""; # rsrc command line my ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) = &NewImageToSymbol(&GetFieldVal( \%REPOSITORY, $keyref, "SrcFile"), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile"), &GetFieldVal( \%REPOSITORY, $keyref, "SrcPath"), &GetFieldVal( \%REPOSITORY, $keyref, "SrcDbg" ));
my( $symsrcfull, $symdstfull ) = &SetSymPaths( &GetFieldVal( \%REPOSITORY, $keyref, "SrcDbg" ), $srcext, $srcpdb, $srcdbg, $srcsym, &GetFieldVal( \%REPOSITORY, $keyref, "DestDbg" ), $dstext );
$pdbline = &MakeXcopyCmd( &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile"), $symsrcfull, $srcpdb, $symdstfull, $dstpdb); $dbgline = &MakeXcopyCmd( &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile"), $symsrcfull, $srcdbg, $symdstfull, $srcdbg); $symline = &MakeXcopyCmd( &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile"), $symsrcfull, $srcsym, $symdstfull, $srcsym);
# in fact, it is necessary to fix the makexcopycmd
$binline = &MakeXcopyCmd( &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile"), &GetFieldVal( \%REPOSITORY, $keyref, "SrcPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile" ) );
# -a or -r switch # localization operation type ( append or replace ) my $rsrctype = &GetLocOpType( &GetFieldVal( \%REPOSITORY, $keyref, "LocOp" ) );
# -l switch my $langsw = &GetLangNlsCode( &GetMacro( "language" ) ); $langsw =~ s/0x0//; $langsw =~ s/0x//;
$langsw = sprintf "-l %s", $langsw;
# -s switch my $symsw = &SetRsrcSymSw( $symsrcfull, $srcdbg, $symdstfull, $dstdbg );
# rsrc command line $rsrcline = sprintf "logerr \"rsrc %s\\%s %s %s\\%s %s %s \"", &GetFieldVal( \%REPOSITORY, $keyref, "DestPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile" ), $rsrctype, &GetFieldVal( \%REPOSITORY, $keyref, "TokPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "TokFile" ), $langsw, $symsw;
# Dependency line $dline = sprintf "%s\\%s : %s\\%s %s\\%s\n", &GetFieldVal( \%REPOSITORY, $keyref, "DestPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile" ), &GetFieldVal( \%REPOSITORY, $keyref, "SrcPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "SrcFile" ), &GetFieldVal( \%REPOSITORY, $keyref, "TokPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "TokFile" );
&PushFieldVal( \%REPOSITORY, $keyref, "Cmd", "$dline" );
# Dependency line done
# Description block
$MAKEDIR{&GetFieldVal( \%REPOSITORY, $keyref, "DestPath" )} = 1;
if ( $dbgline || $pdbline || $symline) { if ( $symdstfull ne &GetFieldVal( \%REPOSITORY, $keyref, "DestDbg" ) ) { $MAKEDIR{$symdstfull}=1; } # if } # if
if ( $pdbline ) { &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$pdbline\n" ) ); } # if
if ( $dbgline ) { &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$dbgline\n" ) ); } # if
if ( $symline ) { &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$symline\n" ) ); } # if
&PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$binline\n" ) ); &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$rsrcline\n" ) );
return;
} # GenRsrcCmd
sub GenMUICmd {
return if $SYNTAX_CHECK_ONLY;
# Optional resource-only DLL generation
my( $keyref ) = @_;
my $lcid = &GetLangNlsCode( &GetMacro( "language" ) );
my( $muiline, $lnkline ) = ( "", "" );
my $_target = &GetMacro( "_target" ),
# Sets the muibld command $muiline = sprintf "logerr \"muibld.exe -l %s -i 4 5 6 9 10 11 16 HTML 23 1024 2110 %s\\%s %s\\mui\\%s\\res\\%s.res\"", $lcid, &GetFieldVal( \%REPOSITORY, $keyref, "DestPath" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile" ), &GetMacro( "_NTBINDIR" ), &GetMacro( "Language" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile" );
# Sets the link command $lnkline = sprintf "logerr \"link /noentry /dll /nologo /nodefaultlib /out:%s\\mui\\%s\\$_target\\%s.mui %s\\mui\\%s\\res\\%s.res\"", &GetMacro( "_NTBINDIR" ), &GetMacro( "Language" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile" ), &GetMacro( "_NTBINDIR" ), &GetMacro( "Language" ), &GetFieldVal( \%REPOSITORY, $keyref, "DestFile" );
$MAKEDIR{sprintf("%s\\mui\\%s\\res", &GetMacro("_NTBINDIR"), &GetMacro("Language"))} = 1;
$MAKEDIR{sprintf("%s\\mui\\%s\\$_target", &GetMacro("_NTBINDIR"), &GetMacro("Language"))} = 1;
&PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$muiline\n" ) ); &PushFieldVal( \%REPOSITORY, $keyref, "Cmd", sprintf( "\t$lnkline\n" ) );
return;
} # GenMUICmd
# GetBgnCodePageSw # Sets the code path bingen switch (-p) # Input: code page value # Output: bingen -p switch
sub GetBgnCodePageSw {
return "-p $_[0]" unless $_[0]=~/^\-$/; } # GetBgnCodePageSw
# GetBgnICodesSw # Sets the -i bingen switch, if needed # ( primary language id and secondary language id for the input language ) # Input: bingen opetation type (-r or -a) # the input language # Output: -i <pri lang code> <sec lang code> in any of the following cases: # * append token operation (bingen -a) # * the input language is different from USA # (another way of saying that ILANGUAGE is defined and is # different from USA) # Otherwise, return ""
sub GetBgnICodesSw {
my( $bgntype, $ilang ) = @_;
# Input language codes my( $cpage, $lcid, $prilang, $seclang );
# Append operation => set -i # Replace operation and the input language is not USA => -i if ( $bgntype eq "-a" || ( $ilang && lc($ilang) ne "usa" ) ) { if ( !$ilang ) { $ilang = "USA"; } # if ( $cpage, $lcid, $prilang, $seclang ) = @{&GetLangCodes( $ilang )}; return "-i $prilang $seclang"; } # if
return "";
} # GetBgnICodesSw
# SetBgnSymSw # Generates the -m bingen switch # Input: dbg source path, dbg file, dbg destination path, dbg file # Output: string containing the -m bingen switch
sub SetBgnSymSw { return " -m $_[0] $_[2]" if ($_[0] !~ /^\-$/ && $_[2] !~ /^\-$/ && -e "$_[0]\\$_[1]"); } # SetBgnSymSw
# SetRsrcSymSw # Input # dbg source path, dbg file, dbg destination path, dbg file # Output # the -s rsrc switch
sub SetRsrcSymSw { return " -s $_[2]\\$_[3]" if ($_[0] !~/^\-$/ && $_[1] !~/^\-$/ && -e "$_[0]\\$_[1]"); } # SetRsrcSymSw
# GetLocOpType # Sets the localization operation type ( replace or append ) # Input # loc op type as found in the mapping file # Output # loc op type ( -a or -r )
sub GetLocOpType { my $loctype = shift; $loctype |= &GetMacro( "LOC_OP_TYPE" ); if ($loctype){ ($locmatch,$locargs)=($loctype=~/^-([A-z]+)([^A-r]*)/); $locargs=~s/,/ /g; if (exists $LOCOPS{$locmatch}) { ($retstr=$LOCOPS{$locmatch})=~s/\$opts/$locargs/e; return $retstr; } } "-r"; } # GetLocOpType
# GetBgnSw # Returns the bingen switch # Input: bingen op type as found in the mapping file # Output: bingen switch
sub GetBgnSw { $_[0] =~tr/A-Z/a-z/; #here, the BGNSW @contains dash<blah blah blah>. map {return $_[0] if $_[0] eq $_} @BGNSWS; "";
} # GetBgnSw
# GetBgnMultitok # Sets the multitoken input parameter (bingen) # Input: operation type and path to the input token files
sub GetBgnMultitok {
my( $keyref, $bgntype ) = @_;
# Language itokens my $langpath;
# Tok path, tok file my( $itokpath, $itokfile ); $itokpath = &GetFieldVal( \%REPOSITORY, $keyref, "ITokPath" ); $itokfile = &GetFieldVal( \%REPOSITORY, $keyref, "TokFile" );
if ( substr($bgntype,-2) ne "-a" ) { return ""; }
$langpath = sprintf "%s\\%s", $itokpath, &GetMacro( "LANGUAGE" ); if ( -e "$langpath\\$itokfile" ) { return "$langpath\\$itokfile"; }
return "$itokpath\\$itokfile";
} # GetBgnMultitok
# GetBgnMultitok # Returns the filenames for symbol files and the extension for directory # # Input: ($srcfile,$destfile,$srcpath) # source file name, # destination file name, # source file path, # source file symbol path. # # Output: ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) # source file extension, # source pdb file name, # source dbg file name, # source sym file name # destination file extension, # destination pdb file name, # destination dbg file name, # destination sym file name. # # Example: ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) = # &NewImageToSymbol($SrcFile, $DestFile, $SrcPath, $srcDbg);
sub NewImageToSymbol {
my ($srcfile,$destfile,$srcpath, $srcdbg) = @_; my @known=qw(exe dll sys ocx drv); my $checkext = qq(pdb); my $valid = 0; my @ext = qw (pdb dbg sym);
map {$valid = 1 if ($srcfile =~/\.$_\b/i)} @known; my @sym = ();
foreach $name (($srcfile, $destfile)){ $ext=$1 if ($name=~/\.(\w+)$/); push @sym, $ext; foreach $newext (@ext){ my $sym = $name; $sym =~ s/$ext$/$newext/; if ($valid && $sym =~ /$checkext$/) { # >link /dump /headers <binary> |( # more? perl -ne "{print $1 if /\s(\S+\.pdb) *$/im}" ) # >blah-blah-blah.pdb my $testname = join "\\",($srcdbg, $ext, $sym); if (! -e $testname){ # we must get the correct directory to check -e! # if ($FULLCHECK and $srcdbg ne "-"){ print STDERR "LINK /DUMP ... $srcpath\\$srcfile => replace $sym " if $DEBUG; $result = qx ("LINK /DUMP /HEADERS $srcpath\\$srcfile"); # $sym = $1 if ($result =~/\s\b(\S+\.$checkext) *$/im); # $sym =~s/[^\\]+\\//g; $sym = $3 if $result =~/\s\b(([^\\]+\\)+)?(\S+.$checkext) *$/im; print STDERR "with $sym\n" if $DEBUG; # _namematch($srcpdb,$pdb); use it still? } } } push @sym, $sym; } } print STDERR join("\n", @sym), "\n----\n" if $DEBUG; @sym; } # NewImageToSymbol
# GetLangCodes # Read the language codes from codes.txt # Input: language # Output: the language codes ( codepage, primary language id, secondary language id ) # Ex. ($CodePage, $lcid, $PriLangId, $SecLangId) = &GetLangCodes($language);
sub GetLangCodes { return (exists $CODES{uc$_[0]})?$CODES{uc$_[0]}: &FatalError( 1910, $language );
} # GetLangCodes
# GetLangNlsCode #
sub GetLangNlsCode { my @langcodes = @{&GetLangCodes(shift)}; return $langcodes[1];
} # GetLangNlsCode
sub _GetLangNlsCode {
# my($language) = @_; # my @langcodes = @{&GetLangCodes(shift)};
return $langcodes[1];
} # GetLangNlsCode
# GenMakeDir # Write the whole tree to create the target structure. # Input & Output # none # Usage # &GenMakeDir();
sub GenMakeDir {
# Remove the parent folder
my $curdir; my @directories;
for $curdir (keys %MAKEDIR) { if (exists $MAKEDIR{$curdir}) { while ($curdir=~/\\/g) { if (($` ne $curdir) && (exists $MAKEDIR{$`})) { delete $MAKEDIR{$`}; } } } }
# Create the record into REPOSITORY $key{DestPath} = "\\\\\\makedir"; $key{DestFile} = "makedir";
&SetField( \%REPOSITORY, \%key, "DestPath", "\\\\\\makedir" ); &SetField( \%REPOSITORY, \%key, "DestFile", "makedir" );
&PushFieldVal( \%REPOSITORY, \%key, "Cmd", "\\\\\\Makedir\\Makedir : \n"); map({&PushFieldVal( \%REPOSITORY, \%key, "Cmd", "\t\@-md $_ 2\>\>nul\n")} sort keys %MAKEDIR);
return; } # GenMakeDir
# WriteToSyscmd # Creates the SYSCMD nmake command file. # Input # syscmd file name # Output # none
sub WriteToSyscmd {
my( $syscmd ) = @_;
# Target name, as specified in the command line my $tname;
# Indexes my $i;
# Write to syscmd in the following cases: # - sysgen with targets (clean or incremental) # - sysgen incremental without targets, but with changes detected in the description blocks if ( &IsEmptyHash( \%TARGETS ) && ( $CLEAN || ( @SYSCMD == 0 ) ) ) { return; } # if
print "Generating $syscmd...\n"; ( open( SYSCMD, ">"."$syscmd" ) ) || &FatalError( 1007, $syscmd );
print SYSCMD "/A \n";
# Always run \\\Makedir\Makedir print SYSCMD "\\\\\\Makedir\\Makedir ";
# Write targets to syscmd for $tname ( sort keys %TARGETS ) { for ( $i=0; $i < @{$TARGETS{$tname}->{NewDestPath}}; $i++ ) { print SYSCMD " \\\n $TARGETS{$tname}->{NewDestPath}->[$i]\\$tname"; } # for } # for
# For incremental without TARGETS, print targets stored in @SYSCMD # ( tbd - add an assert here: SYSCMD can be non-empty only for incremental without targets) for ( $i=0; $i < @SYSCMD; $i++ ) { print SYSCMD " \\\n $SYSCMD[$i]"; } # for
close( SYSCMD );
return; } # WriteToSyscmd
# LoadReposit # Populate CMD_REPOSITORY according to an existing SYSGEN generated makefile. # For each found key, fill in the {Cmd} field. # Input # makefile name # Output # none # Usage # &LoadReposit();
sub LoadReposit {
my( $makefname ) = @_;
# Contents of an existing makefile my @makefile;
# Line number where the "all" target description line finishes my $line;
# Nothing to do in case of a clean SYSGEN ( !$CLEAN ) || return;
# Nothing to do if the makefile does not exist ( -e $makefname ) || &FatalError( 1003, $makefname );
# Open the makefile ( open( MAKEFILE, $makefname ) ) || &FatalError( 1006, $makefname );
print "Loading $makefname...\n"; # Load makefile's contents @makefile = <MAKEFILE>; close( MAKEFILE );
# Fill in keys according to "all" target found in the makefile $line = &LoadKeys( $makefname, \@makefile );
# Load makefile's description blocks into CMD_REPOSITORY's {Cmd} field. &LoadCmd( $makefname, $line, \@makefile );
return;
} # LoadReposit
# LoadKeys # Loads keys according to the "all" target from the given # SYSGEN-generated makefile. # Input # makefile's name # makefile's contents (reference) # Output # line number following "all" target or # -1 if "all" target was not found # Usage # &LoadKeys( \@makefile );
sub LoadKeys {
my( $makefname, $makefref ) = @_;
# Repository key my %key;
# Indexes my $i;
my %alltarget=();
my $cursection=0;
my $makeflines = $#$makefref;
my ($targetname, $errortarget);
# Skip white lines in the beginnig of makefile
for ( $i=0; $i < $makeflines; $i++ ) { if ( $makefref->[$i] =~ /\S/ ) { last; } } # for
# First non empty line from MAKEFILE must contain "sysgen" target ( ( $i < @{$makefref} && $makefref->[$i] =~ /sysgen\s*:/i ) ) || &FatalError( 1213, $makefname);
# ignore nmake line $i++;
$alltarget{'all'} = 1;
$makeflines = $#$makefref;
while (scalar(%alltarget) ne 0) {
# Error if target was not solved. ( ++$i < $makeflines ) || &FatalError( 1210, "${makefname}(" . join(",", keys %alltarget) . ")" );
# Find the target, such as all for ( ; $i < $makefref ; $i++) {
$errortarget=$targetname = '';
# Suppose only find one item matched ($targetname, $errortarget) = map({($makefref->[$i]=~/^\Q$_\E\ :/)?$_:()} keys %alltarget) if($makefref->[$i] =~ / :/);
# Go to next line if not found next if ($targetname eq '');
# Two target found in same line ($errortarget eq '') || &FatalError( 1215, "${makefname}($targetname, $errortarget, ...)");
# Target was found, move to next line and exit for loop $i++; last;
} # for
# Lookfor its belongs for ( ; $i < $makeflines ; $i++ ) {
last if ($makefref->[$i] !~ /\S/);
# lookfor item(s) in one line for (split(/\s+/, $makefref->[$i])) {
next if ($_ eq ''); last if ($_ eq '\\');
# $_=$makefref->[$i];
# If it is a section name, push it into alltarget hash if ( /\Q$SECTIONNAME\E\d+$/) {
$alltarget{$_} = 1;
# Match the last one of $SECTIONAME, with (\d+)\1 => such as 88 => 8, 1616 => 16, 6464 => 64 $SECTIONS = $1 if (($SECTIONS !~/^\d+$/) && (/\Q$SECTIONNAME\E(\d+)\1/));
# Create it into REPOSITORY } elsif (/(\S*)\\(\S*)/) {
($key{DestPath}, $key{DestFile})=($1, $2); &SetField( \%CMD_REPOSITORY, \%key, "DestPath", $1 ); &SetField( \%CMD_REPOSITORY, \%key, "DestFile", $2 );
# If DestFile is part of TARGETS, store DestPath in TARGETS. if ( &IsHashKey( \%TARGETS, $key{DestFile} ) ) { &AddTargetPath( $key{DestFile}, "OldDestPath", $key{DestPath} ); } # if
} # if
} # for
} # for
delete $alltarget{$targetname};
} # while
return $i;
} # LoadKeys
# LoadCmd # Load the body of the makefile. # Fill in the {Cmd} field for each CMD_REPOSITORY key # Input # makefile name # makefile line following the all target dependency lines # make file contents (reference) # Output # none # Usage # &LoadCmd( 2543, \@makefile );
sub LoadCmd {
my( $makefname, $line, $makefref ) = @_;
# Counters my($i, $j);
# Repository key my %key;
# Description line (one or more file lines, # depending on the existence of custom resources) my $dline;
# Buffer for one line my $buffer;
( $line > @makefile ) || &FatalError( 1212, $makefname);
for ( $i = $line; $i < @{$makefref}; $i++ ) {
# Skip white lines if ( $makefref->[$i] !~ /\S/ ) { next; }
# Identify dependency line and key $makefref->[$i] =~ /(\S*)\\(\S*)\s*: /; $key{DestPath} = $1; $key{DestFile} = $2;
# The key must exist in CMD_REPOSITORY &IsRepositKey( \%CMD_REPOSITORY, \%key ) || &FatalError( 1211, "$key{DestPath}\\$key{DestFile}" );
# Load the description block into the {Cmd} field $dline = "";
# Read first the dependency line. # It might be spread over several lines in the makefile, # but we will make a single line from it. for ( $j=$i; $j < @{$makefref}; $j++ ) {
$dline .= $makefref->[$j];
# Description line ends if the last character is not a continuation line mark $buffer = $makefref->[$j]; $buffer =~ s/\s*//g; if ( substr ( $buffer, -1, 1 ) ne "\\" ) { last; } } # for
&PushFieldVal( \%CMD_REPOSITORY, \%key, "Cmd", $dline ); $i=$j+1;
# Read then the command block. for ( $j=$i; $j < @{$makefref}; $j++ ) {
# Description block ends at the first white line encountered if ( $makefref->[$j] !~ /\S/ ) { last; }
# Load the current command line &PushFieldVal( \%CMD_REPOSITORY, \%key, "Cmd", $makefref->[$j] );
} # for
$i = $j;
} # for
return;
} # LoadCmd
# LoadEnv # Loads the environment variables into MACROS (hash). # Uppercases all the macroname. # Input & Output: none # Usage # &LoadEnv();
sub LoadEnv {
for ( keys %ENV ) { &SetMacro( $_, $ENV{$_}, 0 ); } #for
return;
} # LoadEnv
# GetMacro # Returns the value of a macro. # Input # macroname # Output # macrovalue ( empty string if not defined ) # Usage # $language = &GetMacro("Language");
sub GetMacro {
return $MACROS{uc$_[0]}->{Value};
} # GetMacro
# SetMacro # Sets the value of a macro. # Input # macroname # macrovalue # macrotype (see %MACROS in the beginning of the file # for more details on the types of macros. # Output # none # Usage # &SetMacro( "_BuildArch", "nec_98", 0);
sub SetMacro {
my $varname=uc shift;
# Do not overwrite a macro defined in the command line unless # the same macro is redefined in the command line if ( (!exists $MACROS{$varname}) || ($MACROS{$varname}->{Type} == 0) || ($_[1] == 1)) { ($MACROS{$varname}->{Value}, $MACROS{$varname}->{Type})=@_; }
return;
} # SetMacro
# FatalError # Prints the error and exits. # Input # error code # name of the sysfile where the error occured or any other text # line number on the description file where the error occured or 0 if not the case # Ouput # none # Usage # &FatalError( 1111, "sysfile", 12 ); # &FatalError( 1002, $CODESFNAME, 0 );
sub FatalError { &PrintError( "fatal error", @_); print "Stop.\n"; exit;
} # FatalError
# Error # Prints the error and returns. # Input # error code # name of the sysfile where the error occured or any other text # line number on the description file where the error occured or 0 if not the case # Output # none # Usage # &Error( 2011, $recref->{TokPath}\\$recref->{TokFile});
sub Error {
# Errors from IGNORE macro are not counted if ( &PrintError( "error", @_ ) ) { $ERRORS++; } # if
return;
} # Error
# PrintError # Prints the encountered error with the format # <description filename>(<line>): <fatal error | error> <error_code>: <error_text> # Input # error type ( fatal error or error ) # error code # filename where the error was encountered or other text # line number or 0 # Output # 1 if the error is counted # 0 otherwise # Usage # &PrintError( 1002, $CODESFNAME, 0);
sub PrintError {
my( $errtype, $errno, $file, $line ) = @_;
# Error text my $errtxt;
# Ignore errors my $ignore; my @ivalues;
# Counter my $i;
my $fileline;
# Do not print anything if errno is listed in IGNORE macro $ignore = &GetMacro( "IGNORE" ); $ignore =~ s/\s*//g; @ivalues = split ";", $ignore; for ( $i=0; $i < @ivalues; $i++ ) { if ( $errno == $ivalues[$i] ) { return 0; } # if } # for
$errtxt = "SYSGEN:"; $fileline=""; if ( $file ) { if ( $line ) { $fileline=" $file($line)"; } else { $fileline=" $file"; } } # if
if ( $MAP_ERR{$errno} ) { $fileline .= ": ".$MAP_ERR{$errno}; }
$errtxt .= " $errtype $errno:$fileline";
( open LOGFILE, ">>$LOGFILE" ) || &FatalError( 1007, $LOGFILE ); printf LOGFILE "$errtxt\n"; close LOGFILE;
( open ERRFILE, ">>$ERRFILE" ) || &FatalError( 1007, $ERRFILE ); printf ERRFILE "$errtxt\n"; close ERRFILE ;
select(STDERR); $| = 1; printf "$errtxt\n"; select(STDOUT); $| = 1;
return 1;
} # PrintError
# SumErrors # Displays the number of non-fatal errors found while running SYSGEN. # Runs at the end of sysgen. # Input & Output: none # Usage # &SumErrors();
sub SumErrors {
if ( ! $ERRORS ) { print "\nSYSGEN: No errors found during execution.\n"; } # if else { print "\nSYSGEN: Total Errors: $ERRORS\n"; } # else
return;
} # SumErrors
# CleanLogFiles # For a clean SYSGEN, delete sysgen.log and sysgen.err files. # For an incremental SYSGEN, delete only sysgen.err. # Input # sysfile's directory # Output # none # Usage # &CleanLogFiles();
sub CleanLogFiles {
my( $sysdir ) = @_;
# Set LOGFILE and ERRFILE $LOGFILE = "${sysdir}\\sysgen.log"; $ERRFILE = "${sysdir}\\sysgen.err";
# Delete files if ( $CLEAN && -e $LOGFILE ) { unlink $LOGFILE; } if ( -e $ERRFILE ) { unlink $ERRFILE; }
# Delete existing makefile and syscmd if ( $CLEAN && !$SYNTAX_CHECK_ONLY && -e "$sysdir\\makefile" ) { unlink "$sysdir\\makefile"; } if ( -e "$sysdir\\syscmd" ) { unlink "$sysdir\\syscmd"; }
return;
} # CleanLogFiles
# //////////////////////////////////////////////////////////////////////////////////////// # PrintHelp # Print usage
sub PrintHelp {
print STDERR <<EOT; Usage sysgen [<options>] [<macros>] [<targets>] where
<options> are used for controlling the sysgen session. Options are not case sensitive and can be preceded by either a slash (/) or a dash (-). Sysgen recognizes the following options: /c generate the makefile from scratch, overwriting existing one. By running nmake, all the targets will be generated. /s limit sysgen to syntax check - makefile not (re)generated. /f <filename> takes <filename> as the sysfile. If this option is omitted, sysgen searches the current directory for a file called sysfile and uses it as a description (mapping) file.
<macros> list the command line macro definitions in the format "<macroname>=<macrovalue>". Use them if you need to overwrite macros defined in the sysfiles or in the razzle.
<targets> list files (name only, without path) to localize/aggregate. By running nmake, only the specified targets will be generated.
EOT
} #PrintHelp
# FillFilter # Fills in the FILTER variable # Input & Output # none
sub FillFilter {
# List of filtered files my @farray;
# Index my $i;
$file = &GetMacro( "FILTER" );
( $file ) || return; ( -e $file ) || &FatalError( 1005, $file );
# Open the mapping file ( open( FILE, $file ) ) || &FatalError( 1006, $file );
# Load file contents @farray = <FILE>; close( FILE );
print "Loading filter $file...\n";
for ( $i = 0; $i < @farray; $i++ ) {
chop $farray[$i]; $farray[$i] =~ s/^\s*//g; $farray[$i] =~ s/\s*=\s*/=/g; next if (($farray[$i]=~/^\;/)||($farray[$i]=~/^\s*$/)); $FILTER{lc( $farray[$i] )} = 0;
} # for
# In case targets were specified in the command line, # verify FILTER contains them. if ( ! &IsEmptyHash( \%TARGETS ) ) { for ( keys %TARGETS ) { if ( ! exists $FILTER{lc( $_ )} ) { &FatalError( 1009, $_ ); } # if } # for } # if
return;
} # FillFilter
# GetSysdir # Returns the directory name from a sysfile path. # Sysfile's directory is the place where SYSGEN generates the following files: # makefile (used by nmake to execute the aggregation) # syscmd (is the nmake command-line file used for incremental builds) # sysgen.log (the output of SYSGEN and NMAKE) # sysgen.err (the errors found while running SYSGEN and NMAKE) # Input # full path to a sysfile # Output # directory name of the given sysfile
sub GetSysdir {
my( $sysfile ) = @_;
# Output my $sysdir;
# Indexes my $i;
# Buffer my @array;
$sysdir = "."; @array = split /\\/, $sysfile;
if ( @array > 1 ) { $sysdir = ""; for ( $i = 0; $i < (@array-1)-1; $i++ ) { $sysdir .= "$array[$i]\\"; } $sysdir .= $array[$i]; } # if
return $sysdir;
} # GetSysdir
# ParseCmdLine # Parses the command line. # SYSGEN's command-line syntax is: # SYSGEN [<options>] [<macros>] [<targets>] # Input # command-line array # Output # none # Usage # &ParseCmdline(@ARGV);
sub ParseCmdLine {
my @cmdline = @_;
# Indexes my( $i );
my( $optname );
my( @text );
# read makefile.mak if exists if (-e "makefile.mak") { open(F, "makefile.mak"); @text=<F>; close(F);
## $#text + 1 // The lines of the makefile.mak is ## = 1 + 2 + 4 + ... + $total * 2 // the first 1 is the all: target in makefile.mak ## // the remains are the total of process(n)'s line, ex: process04 has 8 lines ## <Mathmetic calculate> ## => $#text = 2 + 4 + ... + $total * 2 // remove + 1 in two side ## => $#text = 2 * (1 + 2 + ... + $total) // take 2 out of the sum serious ## => $#text = 2 * ((1 + $total) * $total) / 2 // the formula of the sum serious ## => $#text = (1 + $total) * $total // remove *2 & /2 ## => $#text = $total^2 + $total // expand ## => $total^2 + $total - $#text = 0 // get =0 equation ## => $total = (-1 + sqrt(1^2 - 4 * 1 * (-$#text)) / (2 * 1) // the roots is (-b + sqrt(b^2 - 4*a*c) / 2a. ignore the negative one ## => $total = (sqrt(4 * $#text + 1) -1) / 2
$DEFAULT_SECTIONS= (sqrt(4 * $#text + 1) - 1) / 2;
} else { $DEFAULT_SECTIONS = 16; }
print "\n"; for ( $i=0; $i < @cmdline; $i++ ) {
$_ = $cmdline[$i];
($optname)=m/[-\/]([\?cnfs])/i;
# Check Option if ( $optname ) {
$SYNTAX_CHECK_ONLY = 1 and next if $optname eq 's'; # -s for syntax check only
if ($optname eq '?') { # -? for help
&PrintHelp(); exit;
} elsif ($optname eq 'c') { # -c for CLEAN
$CLEAN = 1; $SECTIONS = $DEFAULT_SECTIONS if ($SECTIONS !~/^\d+$/); next;
} elsif ($optname eq 'n') { # -n for Section Number
# Set SECTIONS value $i++; ( $i < @cmdline ) || &FatalError( 1008 );
$SECTIONS = $cmdline[$i];
next;
} elsif ($optname eq 'f') { # -f for specified SYSFILE
# Set SYSFILE value $i++; ( $i < @cmdline ) || &FatalError( 1008 );
push @SYSFILES, $cmdline[$i];
next;
} # if } # if
# macro definition if ( /(\S+)\s*\=\s*(\S+)/ ) { &SetMacro( $1, $2, 1 ); next; } # if
# default (target name) &AddTarget($_);
} # for
return;
} # ParseCmdLine
# parseSymbolCD # create the mapping for some binaries listed in symbolcd.txt # Input # filename [path to symbolcd.txt] # Output # REF TO ARRAY OF HASH REF [{BIN => SYMBOL}, ... ] # Sample usage: # print join "\n", @{&parseSymbolCD("symbolcd.txt")}; #
sub parseSymbolCD{
my $fname = shift; print "Loading $fname... "; open (F, "<". $fname); my %o; my $o = \%o; while(<F>){ chomp; my @s = split ",", $_; $s[0] =~ s/^.*\\//g; next if ($s[0] eq $s[1]); $s[1] =~ s/^.*\.//g; $o->{$s[0]} = {} unless defined ($o->{$s[0]}); $o->{$s[0]}->{$s[1]} = $s[2]; # there are more lines } close(F); print scalar(keys(%o)), " symbols\n"; if ($DEBUG){ foreach $lib (keys(%o)){ my %hint = %{$o{$lib}}; print STDERR join("\t", keys(%hint)), "\n"; } }
%o; }
# LoadHotFix # Reads and loads the makefile style hotfix, using the two parts # of the dependancy rule for: # # * check for token->binary depenancy during repository generation # * repository modification # # Input # HOTFIX filename # Output # <none> # LoadHotFix can be called any time, # since it expands symbols without relying on # that vars are defined. # Input # filename [path to hotfix file] # Output # <unused> sub LoadHotFix{ my $filename = shift; return unless -e $filename; open (HOTFIX, $filename); # makefile style hot fix. my ($target, $build, $depend); my $hotfix = \%hotfix; while(<HOTFIX>){ chomp; next if /^\#/; # comment if (/^\S*SET/i){ &SetMacro( &SetAttribOp( $_ ), 0 ); next; } if ($_=~/^(.*) *\:[^\\](.*)$/){ $target = $1;#target: source list my @depend = (); $depend = $2; map {push @depend, lc(&ReplaceVar($_))} split( /\s+/, $depend); $target =~s/ +$//g; $target = lc(&ReplaceVar($target)); $hotfix{$target} = {"build" => [], "depend" => [@depend]}; print STDERR join("\n", map {"'$_'"} @depend), "\n---\n" if $DEBUG; $build = $hotfix->{$target}->{"build"}; } push @$build, "\t".&ReplaceVar($_)."\n" if (/\S/ && /^\t/ );# instructions }
print "Loading $filename ... ", scalar (keys(%hotfix)), " hotfix rules\n"; print STDERR join("\n", map {"'$_'"} keys(%hotfix)), "\n" if $DEBUG; close(HOTFIX);
map {print STDERR $_, "\n",join("\n", @{$hotfix->{$_}->{"build"}}), "\n---\n"} keys(%hotfix) and exit if $DEBUG;
1; }
# FixRepository # Merges contents of Repository with the commands from the HOTFIX file # on the same target without introducing new targets. # Must be called as late as possible but before the # writing the nmake Makefile # Input # <none> # Output # <unused> sub FixRepository{ my $mapref = \%REPOSITORY; return unless scalar(keys(%hotfix)); for $destpath ( sort keys %{$mapref} ) { for $destfile ( sort keys %{$mapref->{$destpath}} ) { $fullname=lc(join("\\",$destpath, $destfile)); if ($hotfix{lc($fullname)}){ print STDERR "Applying hotfix rule for $fullname\n" if $DEBUG; my $cmdref = $mapref->{$destpath}->{$destfile}->{"Cmd"}; my @cmd = map {$_} @{$cmdref}; my $hotfix = \%hotfix; my $depend = $hotfix->{$fullname}->{"depend"}; my $dep = join(" ", "", @$depend); $cmd[0] =~ s/$/$dep/;# append the dep list $#cmd=0 if &GetMacro("OVERWRITE_DEFAULT_RULE"); my $newcmd = $hotfix->{$fullname}->{"build"}; if (&GetMacro("APPEND")){ # append: push @cmd, @{$newcmd}; } else{ # prepend: my $line0 = shift @cmd; unshift @cmd, @{$newcmd}; unshift @cmd, $line0; } $mapref->{$destpath}->{$destfile}->{"Cmd"} = \@cmd; map {print STDERR "$_\n"} @cmd if $DEBUG; } } }
1; }
|