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.
3
# All rights reserved.
4
# sits@users.sourceforge.net
5
#
6
# This program is free software; you can redistribute it and modify it under
7
# the terms of the GPL.
8
 
9
# Parser object for reading the output for a perforce describe command, such
10
# as:
11
#
12
# p4 describe -du <<changenumber>>
13
#
14
# Still need to handle binary files mentioned in the TOC.
15
 
16
package Codestriker::FileParser::PerforceDescribe;
17
 
18
use strict;
19
use Codestriker::FileParser::UnidiffUtils;
20
 
21
sub _make_chunk ($);
22
sub _retrieve_file ($$);
23
 
24
# Return the array of filenames, revision number, linenumber, whether its
25
# binary or not, and the diff text.
26
# Return () if the file can't be parsed, meaning it is in another format.
27
sub parse ($$$) {
28
    my ($type, $fh, $repository) = @_;
29
 
30
    # Skip initial whitespace.
31
    my $line = <$fh>;
32
    while (defined($line) && $line =~ /^\s*$/) {
33
	$line = <$fh>;
34
    }
35
 
36
    # Array of results found.
37
    my @result = ();
38
 
39
    # The table of contents entries.
40
    my @toc = ();
41
 
42
    # Assume the repository matches this diff, unless we find evidence to
43
    # the contrary.
44
    my $repmatch = 1;
45
 
46
    # Check if this is indeed output from a p4 describe command, by looking
47
    # first for the typical header present.
48
    return () unless defined($line);
49
    return () unless defined($line) && $line =~ /^Change \d+ by .* on .*/;
50
 
51
    # Skip the lines up to the table of contents.
52
    $line = <$fh>;
53
    while (defined($line) && $line !~ /^\.\.\./) {
54
	$line = <$fh>;
55
	return () unless defined $line;
56
    }
57
 
58
    # Now read the initial table of contents entries.  For added or
59
    # removed files, we actually need to fetch the text from the
60
    # repository, as it isn't included in the text of the diff,
61
    # unlike CVS.
62
    while (defined($line) && $line =~ /^\.\.\. (.*)\#(\d+) (.*)$/) {
63
	my $entry = {};
64
	$entry->{filename} = $1;
65
	$entry->{revision} = $2;
66
	$entry->{change_type} = $3;
67
	$entry->{repmatch} = 1;
68
	$entry->{old_linenumber} = 0;
69
	$entry->{new_linenumber} = 0;
70
	$entry->{text} = "";
71
	if ($entry->{change_type} eq 'add') {
72
	    _retrieve_file($entry, $repository);
73
	} elsif ($entry->{change_type} eq 'delete') {
74
	    # Need to retrieve the text of the previous revision number,
75
	    # as the current one is empty.
76
	    $entry->{revision}--;
77
	    _retrieve_file($entry, $repository);
78
	    $entry->{revision}++;
79
	} else {
80
	    # Assume it is an edit, nothing else to do, as the diffs
81
	    # will be included below.
82
	}
83
 
84
	# Add this to the table of contents array.
85
	push @toc, $entry;
86
 
87
	$line = <$fh>;
88
	return () unless defined $line;
89
    }
90
 
91
    # Skip the lines until the first diff chunk.
92
    while (defined($line) && $line !~ /^==== /) {
93
	$line = <$fh>;
94
    }
95
 
96
    # Now read the actual diff chunks.  Any entries not here will be added
97
    # or removed files, the text of which has already (should) have been
98
    # retrieved from the repository.
99
    my $toc_index = 0;
100
    while (defined($line) && $line =~ /^====/) {
101
	# Read the next diff chunk.
102
	return () unless $line =~ /^==== (.*)\#(\d+) \((.*)\) ====$/;
103
	my $filename = $1;
104
	my $revision = $2;
105
	my $filetype = $3;
106
 
107
	# Check if there are any outstanding added/removed entries from the
108
	# toc that need to be processed first.
109
	my $entry = $toc[$toc_index];
110
	while ($entry->{filename} ne $filename) {
111
	    my $chunk = _make_chunk($entry);
112
	    push @result, $chunk;
113
 
114
	    # Check the next TOC entry, if any.
115
	    last if ($toc_index >= $#toc);
116
 
117
	    $toc_index++;
118
	    $entry = $toc[$toc_index];
119
	}
120
 
121
	# Skip the next blank line before the unidiff.
122
	$line = <$fh>;
123
	next unless defined $line;
124
 
125
	if ($filetype =~ /.*text/) {
126
	    # Now read the entire diff chunk.
127
	    # Note there may be an optional '---' and '+++' lines
128
	    # before the chunk.
129
	    my $lastpos = tell $fh;
130
	    if (<$fh> !~ /^\-\-\-/ || <$fh> !~ /^\+\+\+/) {
131
		# Move the file pointer back.
132
		seek $fh, $lastpos, 0;
133
	    }
134
 
135
	    my @file_diffs = Codestriker::FileParser::UnidiffUtils->
136
		read_unidiff_text($fh, $filename, $revision, $repmatch);
137
	    push @result, @file_diffs;
138
	} else {
139
	    # Assume it is a binary file, initialise the chunk from the
140
	    # TOC entry, and flag it as binary.
141
	    my $chunk = _make_chunk($entry);
142
	    $chunk->{binary} = 1;
143
	    push @result, $chunk;
144
	}
145
 
146
	# Move on to the next entry in the TOC.
147
	$toc_index++;
148
 
149
	# Skip the next blank line before the next chunk.
150
	$line = <$fh>;
151
    }
152
 
153
    # Finally, add any remaining TOC netries that are unaccounted for.
154
    while ($toc_index <= $#toc) {
155
	my $chunk = _make_chunk($toc[$toc_index]);
156
	push @result, $chunk;
157
	$toc_index++;
158
    }
159
 
160
    # Return the found diff chunks.
161
    return @result;
162
}
163
 
164
# Make an initial chunk from a toc entry.
165
sub _make_chunk ($) {
166
    my ($entry) = @_;
167
 
168
    my $chunk = {};
169
    $chunk->{filename} = $entry->{filename};
170
    $chunk->{revision} = $entry->{revision};
171
    $chunk->{old_linenumber} = $entry->{old_linenumber};
172
    $chunk->{new_linenumber} = $entry->{new_linenumber};
173
    $chunk->{binary} = 0;
174
    $chunk->{text} = $entry->{text};
175
    $chunk->{description} = "";
176
    $chunk->{repmatch} = $entry->{repmatch};
177
    return $chunk;
178
}
179
 
180
# Retrieve the text specified in $entry from the repository.
181
sub _retrieve_file ($$) {
182
    my ($entry, $repository) = @_;
183
 
184
    eval {
185
	my $added = $entry->{change_type} eq 'add';
186
	my @text = ();
187
	$repository->retrieve($entry->{filename}, $entry->{revision},
188
			      \@text);
189
	if ($#text >= 0) {
190
	    if ($added) {
191
		$entry->{new_linenumber} = 1;
192
	    } else {
193
		$entry->{old_linenumber} = 1;
194
	    }
195
	    for (my $i = 1; $i <= $#text; $i++) {
196
		$entry->{text} .= ($added ? "+" : "-") . $text[$i] . "\n";
197
	    }
198
	}
199
    };
200
    if ($@) {
201
	# Problem retrieving text, assume there is no repository match.
202
	$entry->{repmatch} = 0;
203
    }
204
}
205
 
206
1;