@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] # # 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\ # 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 instead of as the root directory backupdir Supplies directory where files will be backed up to. The directory structure created will look like this: \ backup.log - list of files and their versions \\nt\.. - directories and files If -n is specified, the directory structure will look like this: \\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 # clientfilename # foreach $file (sort keys %clientfilelist) { $clientfile = $clientfilelist{$file}; # # Each client filename is in the form X:\ # 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); }