Subversion Repositories DevTools

Rev

Rev 1295 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1293 dpurdie 1
###############################################################################
2
# Codestriker: Copyright (c) 2001,2002,2003 David Sitsky.  All rights reserved.
3
# sits@users.sourceforge.net
4
#
5
# This program is free software; you can redistribute it and modify it under
6
# the terms of the GPL.
7
 
8
# VSS repository access package.
9
 
10
package Codestriker::Repository::Vss;
11
 
12
use strict;
13
use Cwd;
14
use File::Temp qw/ tmpnam tempdir /;
15
use IO::Handle;
16
 
17
# Switch for emitting debug information.
18
my $_DEBUG = 0;
19
 
20
# Constructor, which takes the username and password as parameters.
21
sub new {
22
    my ($type, $username, $password, $ssdir) = @_;
23
 
24
    my $self = {};
25
    $self->{username} = $username;
26
    $self->{password} = $password;
27
    $self->{ssdir} = $ssdir;
28
    bless $self, $type;
29
}
30
 
31
# Method for wrapping the VSS command if necessary via a perl
32
# invocation so that the SSDIR environment variable is set pointing to
33
# the correct VSS repository.  We can't do this with $ENV since
34
# apache2 doesn't allow us to do this.  We assume perl is in the
35
# PATH.
36
sub _write_vss_command {
37
    my ($self, $cmd, $tmpfile) = @_;
38
 
39
    my $ssdir = $self->{ssdir};
40
 
41
    open (TMPFILE, ">$tmpfile") || die "Can't open $tmpfile: $!";
42
    print TMPFILE "\@echo off\n";
43
    print STDERR "\@echo off\n" if $_DEBUG;
44
    print TMPFILE "set SSDIR=$ssdir\n" if defined $ssdir;
45
    print STDERR "set SSDIR=$ssdir\n" if defined $ssdir && $_DEBUG;
46
    print TMPFILE "$cmd\n";
47
    print STDERR "$cmd\n" if $_DEBUG;
48
    close TMPFILE;
49
}
50
 
51
# Retrieve the data corresponding to $filename and $revision.  Store each line
52
# into $content_array_ref.
53
sub retrieve ($$$\$) {
54
    my ($self, $filename, $revision, $content_array_ref) = @_;
55
 
56
    # VSS command to use.
57
    my $vss = $Codestriker::vss;
58
    $vss =~ s/\//\\/g;
59
 
60
    # Create a temporary directory where all of the temporary files
61
    # will be written to.
62
    my $tempdir;
63
    if (defined $Codestriker::tmpdir && $Codestriker::tmpdir ne "") {
64
	$tempdir = tempdir(DIR => $Codestriker::tmpdir, CLEANUP => 1);
65
    }
66
    else {
67
	$tempdir = tempdir(CLEANUP => 1);
68
 
69
	# Hack alert for windows - temporary directory needs to start
70
	# with a letter, or commands below will fail.  Most people will
71
	# set the temporary directory explicitly in the conf file.
72
	$tempdir = 'C:' . $tempdir if $tempdir =~ /^[\\\/]/o;
73
    }
74
 
75
    # Temporary Batch file for executing VSS commands.
76
    my $tmp_batch_file = "$tempdir/tmp.bat";
77
 
78
    # Retrieve a read-only copy of the file into a temporary
79
    # directory.  Make sure the command output is put into
80
    # a temporary file, rather than stdout/stderr.
81
    my $varg = ($revision =~ /^\d+$/) ? "-V$revision" : "\"-VL$revision\"";
82
    my $command_output = "$tempdir\\___output.txt";
83
    my $cmd = "\"$vss\" get \"$filename\"" .
84
	" -y" . $self->{username} . "," . $self->{password} .
85
	" $varg -I-Y -O\"$command_output\" -GWR -GL\"$tempdir\"";
86
    $self->_write_vss_command($cmd, $tmp_batch_file);
87
    system("\"$tmp_batch_file\"");
88
 
89
    $filename =~ /\/([^\/]+)$/o;
90
    my $basefilename = $1;
91
    if (open(VSS, "$tempdir/$basefilename")) {
92
	for (my $i = 1; <VSS>; $i++) {
93
	    $_ = Codestriker::decode_topic_text($_);
94
	    chop;
95
	    $$content_array_ref[$i] = $_;
96
	}
97
	close VSS;
98
	unlink "$tempdir/$basefilename";
99
    }
100
 
101
    # Remove the temporary directory and batch file.
102
    unlink $tmp_batch_file;
103
    rmdir $tempdir;
104
}
105
 
106
# Retrieve the "root" of this repository.
107
sub getRoot ($) {
108
    my ($self) = @_;
109
    return "vss:";
110
}
111
 
112
# Return a URL which views the specified file and revision.
113
sub getViewUrl ($$$) {
114
    my ($self, $filename, $revision) = @_;
115
 
116
    # Lookup the file viewer from the configuration.
117
    my $viewer = $Codestriker::file_viewer->{$self->toString()};
118
    return (defined $viewer) ? $viewer . "/" . $filename : "";
119
}
120
 
121
# Return a string representation of this repository.
122
sub toString ($) {
123
    my ($self) = @_;
124
    return "vss:" . $self->{username} . ":" . $self->{password};
125
}
126
 
127
# Retrieve the specified VSS diff directly using VSS commands.
128
sub getDiff ($$$$$) {
129
    my ($self, $start_tag, $end_tag, $module_name, $fh, $error_fh) = @_;
130
 
131
    # VSS command to use.
132
    my $vss = $Codestriker::vss;
133
    $vss =~ s/\//\\/g;
134
 
135
    # Currently we only support either start_tag or end_tag being set.
136
    my $tag = '';
137
    $tag = $start_tag if $start_tag ne '' && $end_tag eq '';
138
    $tag = $end_tag if $start_tag eq '' && $end_tag ne '';
139
    $tag = $end_tag if $start_tag ne '' && $end_tag ne '';
140
    return $Codestriker::UNSUPPORTED_OPERATION if $tag eq '';
141
 
142
    # Create a temporary directory where all of the temporary files
143
    # will be written to.
144
    my $tempdir;
145
    if (defined $Codestriker::tmpdir && $Codestriker::tmpdir ne "") {
146
	$tempdir = tempdir(DIR => $Codestriker::tmpdir, CLEANUP => 1);
147
    }
148
    else {
149
	$tempdir = tempdir(CLEANUP => 1);
150
 
151
	# Hack alert for windows - temporary directory needs to start
152
	# with a letter, or commands below will fail.  Most people will
153
	# set the temporary directory explicitly in the conf file.
154
	$tempdir = 'C:' . $tempdir if $tempdir =~ /^[\\\/]/o;
155
    }
156
 
157
    # Temporary Batch file for executing VSS commands.
158
    my $tmp_batch_file = "$tempdir/tmp.bat";
159
 
160
    # Execute the VSS command to retrieve all of the entries in this label.
161
    # Note we can't set SSDIR in the environment, so we need to do that in
162
    # the command below.
163
 
164
    my $ssdir = $self->{ssdir};
165
    my $varg = ($tag =~ /^\d+$/) ? "-V$tag" : "\"-VL$tag\"";
166
    my $cmd = "\"$vss\" dir \"$module_name\"" .
167
	" -y" . $self->{username} . "," . $self->{password} .
168
	" -R $varg -I-Y";
169
    $self->_write_vss_command($cmd, $tmp_batch_file);
170
 
171
    open(VSS, "\"$tmp_batch_file\" |")
172
	|| die "Can't open connection to VSS repository: $!";
173
 
174
    # Collect the list of filename and revision numbers into a list.
175
    my @files = ();
176
    my @versions = ();
177
 
178
    # Initialise this in case module just refers to a single file.
179
    my $current_dir = '';
180
    if ($module_name =~ /^(.*)\/[^\/]+$/o) {
181
	$current_dir = $1;
182
    }
183
 
184
    while (<VSS>) {
185
	if (/^(\$\/.*):$/o) {
186
	    # Entering a new top-level directory.
187
	    $current_dir = $1;
188
	} elsif (/^\$[^\/]/o) {
189
	    # Sub-directory entry which can be skipped.
190
	} elsif (/^\d+ item/o) {
191
	    # Item count line which can be skipped.
192
	} elsif (/^\s*$/o) {
193
	    # Skip blank lines.
194
	} elsif (/^(.*);(\d+)$/o) {
195
	    # Actual file entry with version number.
196
	    push @files, "$current_dir/$1";
197
	    push @versions, $2;
198
	}
199
    }
200
    close VSS;
201
 
202
    # Now for each file, we need to retrieve the actual contents and output
203
    # it into a diff file.  First, create a temporary directory to store the
204
    # files.
205
    for (my $i = 0; $i <= $#files; $i++) {
206
	# Determine if the file is a text file, and if not, skip it.
207
	$cmd = "\"$vss\" properties \"$files[$i]\"" .
208
	    " -y" . $self->{username} . "," . $self->{password} .
209
	    " -I-Y";
210
	$self->_write_vss_command($cmd, $tmp_batch_file);
211
	open(VSS, "\"$tmp_batch_file\" |")
212
	    || die "Unable to run ss properties on $files[$i]\n";
213
	my $text_type = 0;
214
	while (<VSS>) {
215
	    if (/Type:\s*Text/o) {
216
		$text_type = 1;
217
		last;
218
	    }
219
	}
220
	close(VSS);
221
	next if $text_type == 0;
222
 
223
	my $command_output = "$tempdir\\___output.txt";
224
	if ($start_tag ne '' && $end_tag ne '') {
225
	    my $varg = "\"";
226
            $varg .= ($start_tag =~ /^\d+$/) ? "-V${start_tag}~" : "-VL${start_tag}~";
227
            $varg .= ($end_tag =~ /^\d+$/) ? $end_tag : "L${end_tag}";
228
            $varg .= "\"";
229
	    $cmd = "\"$vss\" diff \"$files[$i]\"" .
230
		   " -y" . $self->{username} . "," . $self->{password} .
231
		   " -I-Y -DU3000X5 $varg" .
232
		   " -O\"$command_output\"";
233
	    $self->_write_vss_command($cmd, $tmp_batch_file);
234
	    system("\"$tmp_batch_file\"");
235
	    if (open(VSS, $command_output)) {
236
		while (<VSS>) {
237
		    print $fh $_;
238
		}
239
		close VSS;
240
	    }
241
	} else {
242
	    # Retrieve a read-only copy of the file into a temporary
243
	    # directory.  Make sure the command output is put into
244
	    # a temporary file, rather than stdout/stderr.
245
	    my $varg = ($tag =~ /^\d+$/) ? "-V$tag" : "\"-VL$tag\"";
246
	    $cmd = "\"$vss\" get \"$files[$i]\"" .
247
		   " -y" . $self->{username} . "," . $self->{password} .
248
		   " $varg -I-Y -O\"$command_output\" -GWR -GL\"$tempdir\"";
249
	    $self->_write_vss_command($cmd, $tmp_batch_file);
250
	    system("\"$tmp_batch_file\"");
251
 
252
	    $files[$i] =~ /\/([^\/]+)$/o;
253
	    my $basefilename = $1;
254
	    my @data = ();
255
	    if (open(VSS, "$tempdir/$basefilename")) {
256
		while (<VSS>) {
257
		    push @data, $_;
258
		}
259
		close VSS;
260
		unlink "$tempdir/$basefilename";
261
	    }
262
	    my $data_size = $#data + 1;
263
 
264
	    # Output the file header information in VSS diff format.
265
	    print $fh "Diffing: $files[$i];$versions[$i]\n";
266
	    print $fh "Against: \n\n";
267
	    print $fh "0a1,$data_size\n";
268
	    for (my $index = 0; $index <= $#data; $index++) {
269
		print $fh "> " . $data[$index];
270
	    }
271
	}
272
	unlink $command_output;
273
	print $fh "\n";
274
    }
275
 
276
    # Remove the temporary directory and temporary batch file.
277
    unlink $tmp_batch_file;
278
    rmdir $tempdir;
279
 
280
    return $Codestriker::OK;
281
}
282
 
283
1;