Rev 1295 | Blame | Compare with Previous | Last modification | View Log | RSS feed
################################################################################ Codestriker: Copyright (c) 2001,2002,2003 David Sitsky. All rights reserved.# sits@users.sourceforge.net## This program is free software; you can redistribute it and modify it under# the terms of the GPL.# VSS repository access package.package Codestriker::Repository::Vss;use strict;use Cwd;use File::Temp qw/ tmpnam tempdir /;use IO::Handle;# Switch for emitting debug information.my $_DEBUG = 0;# Constructor, which takes the username and password as parameters.sub new {my ($type, $username, $password, $ssdir) = @_;my $self = {};$self->{username} = $username;$self->{password} = $password;$self->{ssdir} = $ssdir;bless $self, $type;}# Method for wrapping the VSS command if necessary via a perl# invocation so that the SSDIR environment variable is set pointing to# the correct VSS repository. We can't do this with $ENV since# apache2 doesn't allow us to do this. We assume perl is in the# PATH.sub _write_vss_command {my ($self, $cmd, $tmpfile) = @_;my $ssdir = $self->{ssdir};open (TMPFILE, ">$tmpfile") || die "Can't open $tmpfile: $!";print TMPFILE "\@echo off\n";print STDERR "\@echo off\n" if $_DEBUG;print TMPFILE "set SSDIR=$ssdir\n" if defined $ssdir;print STDERR "set SSDIR=$ssdir\n" if defined $ssdir && $_DEBUG;print TMPFILE "$cmd\n";print STDERR "$cmd\n" if $_DEBUG;close TMPFILE;}# Retrieve the data corresponding to $filename and $revision. Store each line# into $content_array_ref.sub retrieve ($$$\$) {my ($self, $filename, $revision, $content_array_ref) = @_;# VSS command to use.my $vss = $Codestriker::vss;$vss =~ s/\//\\/g;# Create a temporary directory where all of the temporary files# will be written to.my $tempdir;if (defined $Codestriker::tmpdir && $Codestriker::tmpdir ne "") {$tempdir = tempdir(DIR => $Codestriker::tmpdir, CLEANUP => 1);}else {$tempdir = tempdir(CLEANUP => 1);# Hack alert for windows - temporary directory needs to start# with a letter, or commands below will fail. Most people will# set the temporary directory explicitly in the conf file.$tempdir = 'C:' . $tempdir if $tempdir =~ /^[\\\/]/o;}# Temporary Batch file for executing VSS commands.my $tmp_batch_file = "$tempdir/tmp.bat";# Retrieve a read-only copy of the file into a temporary# directory. Make sure the command output is put into# a temporary file, rather than stdout/stderr.my $varg = ($revision =~ /^\d+$/) ? "-V$revision" : "\"-VL$revision\"";my $command_output = "$tempdir\\___output.txt";my $cmd = "\"$vss\" get \"$filename\"" ." -y" . $self->{username} . "," . $self->{password} ." $varg -I-Y -O\"$command_output\" -GWR -GL\"$tempdir\"";$self->_write_vss_command($cmd, $tmp_batch_file);system("\"$tmp_batch_file\"");$filename =~ /\/([^\/]+)$/o;my $basefilename = $1;if (open(VSS, "$tempdir/$basefilename")) {for (my $i = 1; <VSS>; $i++) {$_ = Codestriker::decode_topic_text($_);chop;$$content_array_ref[$i] = $_;}close VSS;unlink "$tempdir/$basefilename";}# Remove the temporary directory and batch file.unlink $tmp_batch_file;rmdir $tempdir;}# Retrieve the "root" of this repository.sub getRoot ($) {my ($self) = @_;return "vss:";}# Return a URL which views the specified file and revision.sub getViewUrl ($$$) {my ($self, $filename, $revision) = @_;# Lookup the file viewer from the configuration.my $viewer = $Codestriker::file_viewer->{$self->toString()};return (defined $viewer) ? $viewer . "/" . $filename : "";}# Return a string representation of this repository.sub toString ($) {my ($self) = @_;return "vss:" . $self->{username} . ":" . $self->{password};}# Retrieve the specified VSS diff directly using VSS commands.sub getDiff ($$$$$) {my ($self, $start_tag, $end_tag, $module_name, $fh, $error_fh) = @_;# VSS command to use.my $vss = $Codestriker::vss;$vss =~ s/\//\\/g;# Currently we only support either start_tag or end_tag being set.my $tag = '';$tag = $start_tag if $start_tag ne '' && $end_tag eq '';$tag = $end_tag if $start_tag eq '' && $end_tag ne '';$tag = $end_tag if $start_tag ne '' && $end_tag ne '';return $Codestriker::UNSUPPORTED_OPERATION if $tag eq '';# Create a temporary directory where all of the temporary files# will be written to.my $tempdir;if (defined $Codestriker::tmpdir && $Codestriker::tmpdir ne "") {$tempdir = tempdir(DIR => $Codestriker::tmpdir, CLEANUP => 1);}else {$tempdir = tempdir(CLEANUP => 1);# Hack alert for windows - temporary directory needs to start# with a letter, or commands below will fail. Most people will# set the temporary directory explicitly in the conf file.$tempdir = 'C:' . $tempdir if $tempdir =~ /^[\\\/]/o;}# Temporary Batch file for executing VSS commands.my $tmp_batch_file = "$tempdir/tmp.bat";# Execute the VSS command to retrieve all of the entries in this label.# Note we can't set SSDIR in the environment, so we need to do that in# the command below.my $ssdir = $self->{ssdir};my $varg = ($tag =~ /^\d+$/) ? "-V$tag" : "\"-VL$tag\"";my $cmd = "\"$vss\" dir \"$module_name\"" ." -y" . $self->{username} . "," . $self->{password} ." -R $varg -I-Y";$self->_write_vss_command($cmd, $tmp_batch_file);open(VSS, "\"$tmp_batch_file\" |")|| die "Can't open connection to VSS repository: $!";# Collect the list of filename and revision numbers into a list.my @files = ();my @versions = ();# Initialise this in case module just refers to a single file.my $current_dir = '';if ($module_name =~ /^(.*)\/[^\/]+$/o) {$current_dir = $1;}while (<VSS>) {if (/^(\$\/.*):$/o) {# Entering a new top-level directory.$current_dir = $1;} elsif (/^\$[^\/]/o) {# Sub-directory entry which can be skipped.} elsif (/^\d+ item/o) {# Item count line which can be skipped.} elsif (/^\s*$/o) {# Skip blank lines.} elsif (/^(.*);(\d+)$/o) {# Actual file entry with version number.push @files, "$current_dir/$1";push @versions, $2;}}close VSS;# Now for each file, we need to retrieve the actual contents and output# it into a diff file. First, create a temporary directory to store the# files.for (my $i = 0; $i <= $#files; $i++) {# Determine if the file is a text file, and if not, skip it.$cmd = "\"$vss\" properties \"$files[$i]\"" ." -y" . $self->{username} . "," . $self->{password} ." -I-Y";$self->_write_vss_command($cmd, $tmp_batch_file);open(VSS, "\"$tmp_batch_file\" |")|| die "Unable to run ss properties on $files[$i]\n";my $text_type = 0;while (<VSS>) {if (/Type:\s*Text/o) {$text_type = 1;last;}}close(VSS);next if $text_type == 0;my $command_output = "$tempdir\\___output.txt";if ($start_tag ne '' && $end_tag ne '') {my $varg = "\"";$varg .= ($start_tag =~ /^\d+$/) ? "-V${start_tag}~" : "-VL${start_tag}~";$varg .= ($end_tag =~ /^\d+$/) ? $end_tag : "L${end_tag}";$varg .= "\"";$cmd = "\"$vss\" diff \"$files[$i]\"" ." -y" . $self->{username} . "," . $self->{password} ." -I-Y -DU3000X5 $varg" ." -O\"$command_output\"";$self->_write_vss_command($cmd, $tmp_batch_file);system("\"$tmp_batch_file\"");if (open(VSS, $command_output)) {while (<VSS>) {print $fh $_;}close VSS;}} else {# Retrieve a read-only copy of the file into a temporary# directory. Make sure the command output is put into# a temporary file, rather than stdout/stderr.my $varg = ($tag =~ /^\d+$/) ? "-V$tag" : "\"-VL$tag\"";$cmd = "\"$vss\" get \"$files[$i]\"" ." -y" . $self->{username} . "," . $self->{password} ." $varg -I-Y -O\"$command_output\" -GWR -GL\"$tempdir\"";$self->_write_vss_command($cmd, $tmp_batch_file);system("\"$tmp_batch_file\"");$files[$i] =~ /\/([^\/]+)$/o;my $basefilename = $1;my @data = ();if (open(VSS, "$tempdir/$basefilename")) {while (<VSS>) {push @data, $_;}close VSS;unlink "$tempdir/$basefilename";}my $data_size = $#data + 1;# Output the file header information in VSS diff format.print $fh "Diffing: $files[$i];$versions[$i]\n";print $fh "Against: \n\n";print $fh "0a1,$data_size\n";for (my $index = 0; $index <= $#data; $index++) {print $fh "> " . $data[$index];}}unlink $command_output;print $fh "\n";}# Remove the temporary directory and temporary batch file.unlink $tmp_batch_file;rmdir $tempdir;return $Codestriker::OK;}1;