#!/usr/local/bin/perl

# these two perl library files are part of the standard perl
# installation.  We never "officially" installed perl, so I
# just have copies of these libraries in my local bin directory.
require "/net/home/nrdlan/bin/find.pl";
require "/net/home/nrdlan/bin/dir.pl";

# directories to which we will create links
@dirList = ('src', 'inc', 'pd_make', 'obj', 'lib', 'bin', 'oids', 'ts_dir', 'dep_dir', 'utils', 'pdtest', 'attr', 'vsmlib');
@exclude = ('bin/pdb', 'log_printer', 'notifUT', 'phy_printer', 'syslogUT');

# for os2, we will create directories for all the names listed in @dirList,
# but we only want the files in:
@os2DirList = ('src', 'inc', 'pd_make', 'oids', 'dep_dir');

@generatedFiles = (
'dpo/dpo_if_cstub.c',
'dpo/dpo_if_sstub.c',
'dpo/dpo_if_caux.c',
'dpo/dpo_if_saux.c',
'inc/dpo_if.h',
'inc/GMsg_msg.h',
'help/GMsg.cat',
'inc/GMsg_cat.h',
'common/fpa.cat',
'common/fpahelp.cat',
'oid/oidpiobe.cpp',
'oids/ibmpsf6K.db',
'oids/ibmsbsd.db',
'oids/ibm.db',
'oids/ibmpiobe.db',
'oid/oid_ut.cat',
'oid/ibmpsf6K.cat',
'oid/ibmoid.cat',
'oid/ibmsbsd.cat',
'oid/ibmpiobe.cat',
'oid/oidsbsd.cpp',
'oid/external.db',
'oid/oid_con.cpp',
'oid/oidpsf6K.cpp',
'inc/oid_con.h',
'inc/oid_def.h',
'inc/oidsbsd.h',
'inc/oidpiobe.h',
'inc/oidpsf6K.h',
);

for (@generatedFiles) {
	$ListOfGeneratedFiles{$_} = 1;
}

# directories in out test build area where which might contain files that
# we want to remove
@updateList = ('src', 'inc', 'pd_make', 'obj', 'dep_dir', 'oid_dbs');

$usage = <<"!";
USAGE: $0 [-tar] [-delete] [-from fromDir] [-to toDir] [-flat] [-mkbuild]
  Create links (or copies) of one pool into another test pool
  -from fromDir: directory to link to (copy from).  default: /pd_dev
  -touch: relinks all .o's, .u's, and libraries to fromDir and touchs 
          all local source files
  -to toDir: dirctory to put links in.  default: `pwd`
  -flat: creates a flatsrc directory which points to /src
  -tar: create copies of files.  default, create links
  -delete: delete the directories: 
           (@dirList)
           in the toDir.  Used if you want to start from scratch.
  -clean: checks to see if links in toDir point to something.
  -mkbuild: run the mkbuild tool upon completion of link creation
  -os2: creates copies of files in the @os2DirList.  os2 does not
        recognize symbol links, so we will copy the minimum files
        files needed to perform an os2 build and create all the
        necssary directories.
 -force: forces the link to be recreated whether it exists or not.
           (to be used if currently linked to pd_dev and then want
            to link to pd_int.  This will force existing links to
            be recreated)

  EXAMPLES:
  $0                # create links from pd_dev in pwd
  $0 -touch         # clean out all .o's, .u's, and libs, then relink
  $0 -from /pd_int  # create links to pd_int in pwd
!

while (@ARGV) {
	$_ = shift(@ARGV);
	
	if (/-tar/) {
		$tar = 1;
	}
	elsif (/-os2/) {
		# we need to create all the necessary directories to build
		for (@dirList) {
			if (! -e $_) {
				system "mkdir $_";
			}
		}

		# we only need a subset of the files
		@dirList = @os2DirList;

		# we are going to copy files from the $fromDir
		$os2 = 1;
	}
	elsif (/-delete/) {
		$remove = 1;
	}
	elsif (/-mkbuild/) {
		$mkbuild = 1;
	}
	elsif (/-clean/) {
		$clean = 1;
	}
	elsif (/-flat/) {
		$flat = 1;
	}
        elsif (/-force/) {
               $forceLink = 1;
        }
	elsif (/-from/) {
		$fromDir = shift(@ARGV);
	}
	elsif (/-touch/) {
		$touch = 1;
	}
	elsif (/-to/) {
		$toDir = shift(@ARGV);
		if (! -d $toDir) {
			system "mkdir $toDir";
		}
	}
	elsif (/\?/ || /-\?/) {
		die $usage;
	}
}

if (! $fromDir) {
	$fromDir = '/pd_dev';
}

if (! $toDir) {
	$toDir = `pwd`;
	chop($toDir);
}

$here = `pwd`;
chop($here);

if ($touch) {
	print "Removing .o's, .u's, and libraries ...\n";
	chdir $toDir;
	system "rm -fr obj/*";
	system "rm -fr dep_dir/*";
	system "rm -fr lib/*";
	chdir $here;
}

if ($remove) {
	chdir $toDir;
	system "rm -fr @dirList";
	system "rm -fr flatsrc";
	chdir $here;
	exit;
}

if ($tar) {
	 system "cd $fromDir; tar cf - @dirList | (cd $toDir; tar xf -)";
}
else {
	$flatDir = "$toDir/flatsrc";
	if ($flat) {
		if (! -d $flatDir) {
			print "creating directory: $flatDir...\n";
			system "mkdir $flatDir";
		}
	}

	# if there is a src directory assume we are simply updating
	# lets accumulate interesting data like existing objects,
	# existing files, etc.
	chdir $toDir;
	if (-d "src" && ! $os2) {
		$update = 1;
		print "Found a \'src\' directory.  Assuming update.\n";
		print "Linking files from: $fromDir\n";
		open(FND, "find @updateList -print |");
		$| = 1;
		while (<FND>) {
			chop;
			$fullName = $_;
			@toks = split(/\//, $_);
			$key2 = pop(@toks);
			$key1 = pop(@toks);
			$key = "$key1/$key2";
			$fullName =~ s/$toDir//;

			# look for real files, ignore links and directories
			lstat($_);
			if (-f _ && ! -l _ && ! -d _) {
				# keep track of local .o's
				($dev, $ino, $mode) = lstat($_);
				if (/\.o$/) {
					$localObjects{$_} = 1;
				}
				# keep track of local .u's
				elsif (/\.u$/) {
					$localDependencies{$_} = 1;
				}
				# keep track of files which are read only (assume
				# that these have been checked in)
				elsif ($mode == 33060) {
					$checkedIn{$_} = 1;
				}
				elsif ($ListOfGeneratedFiles{$key}) {
					$generatedFiles{$fullName} = 1;
				}
				# keep track of local .cpp's and other local files
				else {
					$localFiles{$key} = $fullName;
				}
			}
			elsif (-l _ && $clean) {
				stat($_);
				if (! -e _) {
					push(@emptyLinks, $_);
				}
			}

			# the files which we currently have accounted for in
			# our test build pool
			$accountedFor{$key} += 1;
		}
	}

#	&printData;

	# for each directory in @dirList in the fromDir, perform find and
	# execute subroutine 'wanted' (special perl find functionality)
	foreach (@dirList) {
		$dir = "$fromDir/$_";
		&find($dir);
	}

	if ($update && $newFile) {
		print "Files exist in the pool which are not in your toDir.\n";
		print "The following new links have been created:\n";
		for (@newFiles) {
			print "   $_\n";
		}
	}
	else {
		print "No new files have been added ...\n";
	}

	if ($mkbuild) {
		print "Running mkbuild ...\n";
		chdir $toDir;
		system "mkbuild";
	}
}

if ($os2) {
	&printOs2Results;
}
else {
	&printResults;
	&fixUp;
}


# subroutines

sub wanted {
	# the perl find utility store the full path name to the file in
	# $name, the current directory in $dir, and the base file name in $_
	$toName = $name;
	$toName =~ s/$fromDir/$toDir/;
	$base = $_;
	@toks = split(/\//, $dir);
	$subDir = pop(@toks);
	foreach $exclude (@exclude) {
		if ($name =~ /$exclude/) {
			return;
		}
	}

	# keep track of mulitple uses of same name (e.g. Makefile)
	$names{$base} += 1;
	
	# if directory, create a new directory here in which we will put
	# links which point to files in fromDir directory.
	if (-d ) {
		if (! -d $toName) {
			print "creating directory: $toName...\n";
			system "mkdir $toName";
		}
	}
       # if the from name is a symbolic link and we do not have a
       # toName symbolic link, cp the link to toName.  This is
       # for files like pdrm which are links to pdcommand.
       #elsif (-l $name && ! $os2) {
       #       # if we do not have a local link
       #       if (! -e $toName && ! -l $toName) {
       #               system "cp -R $name $toName";
       #       }
       #}
       # if the real file already exists in the to directory, we do not
       # do anything except save the source file name in case we want
       # to touch the file later.
       elsif  (-e $toName && ! -l $toName && ! -d $toName && ! $os2) {
               # if we are doing a "touch", save names of src files in the
               # to directory to touch after we have relinked all the
               # .o files.  This will force recomplies.
               if ($toName =~/src/ || $toName =~ /inc/) {
                       push(@touchedFiles, $toName);
		}
          }

		# otherwise, create the link
       # else we want to create a link in the to directory to this file
       # in the from directory.
       else {
		$_ = $name;

		# if updating, keep track of new files added in fromDir just for
		# interests sake.
		if ($update) {
			if (/\.cpp$/ || /\.h$/ || /\.c$/ || /\.db$/) {
				@toks = split(/\//, $_);
				$key2 = pop(@toks);
				$key1 = pop(@toks);
				$key = "$key1/$key2";
				if (! $accountedFor{$key}) {
					$newFile = 1;
					push(@newFiles, $toName);
				}
			}
		}

		if ($os2) {
			# for os2 we want to:
			# 1: create new copies of files which do not already exist
			# 2: if a copy already exists, we want to get a new copy
			#    if there is a difference between the os2 copy and the
			#    pool, but only if there is no .ext or .co file.

			# if file in os2 does not exist, create copy
			if (! -e $toName) {
				system "cp $name $toName";
				system "chmod 666 $toName";
			}

		}
		else {
                if (! -l $toName || $forceLink) {
                               system "ln -fs $name $toName";
                   }
		}

		# if we also want a flatsrc directory,  create links from
		# from flatsrc to files in src/
		if ($flat && ($name =~ m,/src/, || $name =~ m,/inc/,) ) {
			$flatName = $base;
			if ($names{$base} > 1) {
				@tokens = split(/\//, $name);
				pop(@tokens);	
				$s_dir = pop(@tokens);
				$flatName = "$flatName-$s_dir";
			}
			$flatName = "${toDir}/flatsrc/${flatName}";
			if (! -l $flatName) {
				system "ln -s $toName $flatName";
			}
		}
	}
}
sub notused_code {

                       # if we have an object file, get a list of the files
                       # which the .o depends on from the .u file
                       if (/\.o/) {
                               $objectFile = $_;
                               $correspondingDepFile = $objectFile;
                               $correspondingDepFile =~ s/\.o/.u/;
                               ($depFile, $suffix) = split(/\./, $_);
                               $depFile = "dep_dir/$depFile.u";
                               if (open(DF, "$toDir/$depFile")) {
                                       %dependencies = ();
                                       while (<DF>) {
                                               ($a, $b) = split(/\s+/, $_);
                                               @a = split(/\//, $b);
                                               $dep = pop(@a);
                                               $dependencies{$dep} = 1;
                                       }
                                       close(DF);

                                       $canRemove = 1;
                                       # cycle over list of files which we havelocal
                                       for (keys %localFiles) {
                                               local($dir, $file) = split(/\//,$_);
                                               # if our .o depends on one of those files, then
                                               # we can not remove it.
                                               if ($dependencies{$file}) {
                                                       $canRemove = 0;
                                                       last;
                                               }
                                       }


                                       # if we can not remove it ...
                                       if ($canRemove == 0) {
                                               # keep track of the .o and its .u file
                                               $retainedObjects{"obj/$objectFile"} = 1;
                                               $correspondingDep = $objectFile;
                                               $correspondingDep =~ s/\.o/\.u/;
                                               $correspondingDep = "dep_dir/$correspondingDep";
                                               $retainedDependencies{$correspondingDep} = 1;
                                       }
                                       else {
                                               $canRemoveObj{"obj/$objectFile"}
 = 1;
                                               $canRemoveDep{"dep_dir/$correspondingDepFile"} = 1;
                                       }
                               }
                       }
}
sub printData
{
	print "Local objects:\n";
	for (keys %localObjects) {
		print "  $_\n";
	}
	print "Local dependencies:\n";
	for (keys %localDependencies) {
		print "  $_\n";
	}
	print "Read only files:\n";
	foreach $key (keys %checkedIn) {
		print "  $key\n";
	}
	print "Generated Files:\n";
	for (keys %generatedFiles) {
		print "  $_\n";
	}
	print "Local files:\n";
	for (keys %localFiles) {
		print "  $_\n";
	}
}

sub printOs2Results {
	print "RESULTS ...\n";
	print "Local files checked out:\n";
	for (@os2CheckedOut) {
		print "  $_\n";
	}
	print "Local files extracted:\n";
	for (@os2Extracted) {
		print "  $_\n";
	}
	print "Files updated from pool:\n";
	for (@os2Updated) {
		print "  $_\n";
	}
}

sub printResults {
	print "RESULTS ...\n";
	print "Local files:\n";
	for (keys %localFiles) {
		print "  $_\n";
	}
	print "Retained Objects:\n";
	for (keys %retainedObjects) {
		print"  $_\n";
	}
	print "Read only files:\n";
	for (keys %checkedIn) {
		print "  $_\n";
	}
	print "Generated files:\n";
	for (keys %generatedFiles) {
		print "  $_\n";
	}
	print "Updated files:\n";
	for (keys %updatedFiles) {
		print "  $_\n";
	}
	@objs = keys %canRemoveObj;
	if (@objs) {
		print "There are local objects which can be relinked\n";
	}
	if (@emptyLinks) {
		print "There are empty links which can be removed\n";
	}

#	print "Objects to be Removed:\n";
#	for (keys %canRemoveObj) {
#		print"  $_\n";
#	}
#	print "Touched Files: \n";
#	for (@touchedFiles) {
#		print"  $_\n";
#	}
#	print "Empty links:\n";
#	for (@emptyLinks) {
#		print "  $_\n";
#	}
}

       # Guarantee that the links for pdcommand in bin are correct
       @pdcommands = (pdclean, pdcreate, pddelete, pddisable, pdenable, pdgate,
 pdls, pdmod, pdpause, pdpr, pdpromote, pdq, pdresubmit, pdresume, pdrm, pdset,
pdshutdown, pdstopd);
       for (@pdcommands) {
               system "ln -fs pdcommand ${toDir}/bin/$_";
       }

sub fixUp {
	$out = "$toDir/mkpdlinks.run";
	print "Creating script file: $out\n";
	open(O, ">$out") || die "Could not open $out.  Stopped at";
	system "chmod +x $out";
	select O;
	print "toDir=$toDir\n";
	print "fromDir=$fromDir\n";
	print "\n# Files to touch: (from -touch option)\n";
	for (@touchedFiles) {
               if ($touch) {
                       system "touch $_";
               }
               else {
                       print "touch $_\n";
               }
	}
	print "\n# Objects to be relinked:\n";
	for (keys %canRemoveObj) {
		print "ln -fs \$fromDir/$_ \$toDir/$_\n";
	}
	print "\n# Dependency files to be relinked:\n";
	for (keys %canRemoveDep) {
		print "ln -fs \$fromDir/$_ \$toDir/$_\n";
	}
	print "\n# Read only files (presumably checked in):\n";
	for (keys %checkedIn) {
		print "ln -fs \$fromDir/$_ \$toDir/$_\n";
	}
	print "\n# Generated files (presumably don't need local):\n";
	for (keys %generatedFiles) {
		print "ln -fs \$fromDir/$_ \$toDir/$_\n";
	}
	print "\n# Empty links which point to nothing: (from -clean option)\n";
	for (@emptyLinks) {
		print "rm $_\n";
	}
	print "\n# Local files (uncomment lines which may apply)\n";
	for (keys %localFiles) {
		$fullFile = $localFiles{$_};
		print "# FILE: $_\n";
		print "#ln -fs \$fromDir/$fullFile \$toDir/$fullFile\n";
		print "#rm $toDir/$fullFile\n";
		print "\n";
	}
}
	
