Windows2003-3790/tools/sdbackup.cmd

240 lines
7.0 KiB
Batchfile

@echo off
for %%i in (%0.cmd) do perl -w -x %%~dp$PATH:i%0.cmd %*
goto :eof
#!perl
# line 6
#
# FileName: sdbackup.cmd
#
# Usage = sdbackup [-v] [-d altdir] <backupdir>
#
# Enumerates all the SD opened files in all depots and copies the
# changes out to the specified path. This can be useful simply as
# a regular backup of local changes, or as a mechanism for sharing
# private changes with another person.
#
# Example:
# sdbackup \\scratch\scratch\jvert\sdbackup
# copies all opened files to \\scratch\scratch\jvert\sdbackup\<currentdate>
# sdbackup -d newchange \\scratch\scratch\jvert
# copies all opened files to \\scratch\scratch\jvert\newchange
#
#
use Getopt::Std;
use File::Path;
use File::Spec;
use File::Copy;
#
# initialize globals
#
$opt_v = 0;
$opt_d = 0;
%revisionlist = ();
%changelist = ();
%clientfilelist = ();
$Usage = "
USAGE: $0 [-v] [-d altdir] [backupdir]
-v Verbose output
-d altdir use <altdir> instead of <currentdate> as the root directory
backupdir Supplies directory where files will be backed up to.
The directory structure created will look like this:
<backupdir>\<currentdate>
backup.log - list of files and their versions
\\nt\.. - directories and files
If -n is specified, the directory structure will look
like this:
<backupdir>\\nt.. - directories and files
If not specified, the filenames will just be printed to stdout.\n";
#
# Finds the full filenames of all checked out files
#
getopts('vd:') or die $Usage;
$backupdir = $ARGV[0];
$opt_v and print("backupdir is $backupdir\n");
foreach $line (`sdx opened`) {
chomp $line;
#
# throw away blank lines, depot specifiers, and empty lists
#
$line =~ /^[\t\s]*$|^------|^File\(s\) not opened/ and next;
#
# throw away files that aren't in the default change list. Kind of sleazy,
# but it prevents the public libraries from getting in the list
#
$line =~ /edit change \d/ and next;
#
# throw away deleted files
#
$line =~ /delete default change/ and next;
#
# If we hit the summary, we're done
#
$line =~ /^== Summary/ and last;
$opt_v and print ("line = $line\n");
#
# parse the resulting filename
#
($depotfilename, $revision, $change) = split(/#| - /,$line);
$opt_v and print ("\tfilename: $depotfilename\n\trevision: $revision\n\tchange: $change\n\n");
$depotfilename = lc($depotfilename);
$revisionlist{$depotfilename} = $revision;
$changelist{$depotfilename} = $change;
}
#
# Now we have a list of depot names. Translate those into clientnames
# by calling sdx where
#
# note: since the command line can only accept a limited # of files
# to run "sdx where" on, we break up the command into multiple
# smaller "sdx where" runs.
#
$s = "";
$r = [keys(%revisionlist)];
$maxArgSize = 200;
foreach $revision (0..$#$r) {
# get the combination of the existing list + the current revision
$k = $s." ".$$r[$revision];
# if the list is too long then it's time to process it
# if we are at the end of the list, then we need to flush the list
if ((length($k) > $maxArgSize) || ($revision == $#$r)) {
#
# pass:
# 0: do the existing list in $s
# 1: do the current revision
#
$firstPass = 1;
while(1) {
# skip first iteration if $s is empty
# this implies that the current revision
# is > 200 chars and may not work
if ($s ne "") {
if (length($s) > $maxArgSize) {
print "warning: attempting to process long revision: $revision\n";
}
foreach $line (`sdx where $s`) {
#
# throw away blank lines, depot specifiers, "not found" lines,
# and lines that start with "-"
#
$line =~ /^[\t\s]*$|^-------|file\(s\) not in client view|^-/ and next;
chomp $line;
$opt_v and print("mapping: $line\n");
($depotfilename, $trash, $clientfilename) = split(/\s/,$line);
$depotfilename = lc($depotfilename);
$clientfilelist{$depotfilename} = lc($clientfilename);
-e $clientfilename or die "file: $clientfilename should exist but doesn't!\n";
}
}
if (! $firstPass) {
last;
}
$s = $$r[$revision];
$firstPass = 0;
}
$s = "";
} else {
$s = $k;
}
}
#
# make sure we have as many client file list entries as revision entries
#
$temp = [(keys %clientfilelist)];
if ($#$temp != $#$r) {
print "error occured while compiling client file list\n";
print $#$temp ." vs. ". $#$r ."\n";
die;
}
if ($backupdir) {
if ($opt_d) {
$backupdir .= "\\$opt_d";
} else {
@time = localtime;
$backupdir .= sprintf("\\%4d-%02d-%02d.%02d%02d",($time[5]+1900),$time[4]+1,$time[3],$time[2],$time[1]);
$opt_v and print $backupdir."\n";
}
mkpath($backupdir) unless -d $backupdir;
open(LOGFILE, "+> $backupdir\\backup.log") or die "Couldn't open $backupdir\\backup.log: $!\n";
#
# Write all the files to backup.log. Format is:
# SDfilename #<versionnumber> clientfilename
#
foreach $file (sort keys %clientfilelist) {
$clientfile = $clientfilelist{$file};
#
# Each client filename is in the form X:<full pathname>\<filename>
# Split this up into volume, path, filename components (we don't
# care about the volume)
#
($trash,$dir,$filename) = File::Spec->splitpath($clientfile);
#
# Write this file's data to the logfile
#
$srcfile = File::Spec->catfile($dir, $filename);
print LOGFILE "$file #$revisionlist{$file} $srcfile\n";
#
# compute the destination directory and make sure it exists.
#
$destdir = File::Spec->catdir($backupdir, $dir);
-d $destdir || mkpath($destdir) or die "Couldn't create destination directory $destdir: $!\n";
#
# compute the full destination path and filename
#
$destfile = File::Spec->catfile($destdir, $filename);
if ($opt_v) {
print("copying $clientfile to $destfile\n");
} else {
print("$clientfile\n");
}
copy($clientfile, $destfile, 0)
or die "Couldn't copy $clientfile -> $destfile: $!\n";
}
} else {
print join("\n", sort values %clientfilelist);
}