This file appears to be unnecessary.
[cvs2svn.git] / cvs2svn_lib / rcs_stream.py
blobb893819533307d78a63a0f13f9cb715cba1e156b
1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2007 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """This module processes RCS diffs (deltas)."""
20 import re
22 def msplit(s):
23 """Split S into an array of lines.
25 Only \n is a line separator. The line endings are part of the lines."""
27 # return s.splitlines(True) clobbers \r
28 re = [ i + "\n" for i in s.split("\n") ]
29 re[-1] = re[-1][:-1]
30 if not re[-1]:
31 del re[-1]
32 return re
35 class MalformedDeltaException(Exception):
36 """A malformed RCS delta was encountered."""
38 pass
40 class RCSStream:
41 """This class represents a single file object to which RCS deltas can be
42 applied in various ways."""
44 ad_command = re.compile(r'^([ad])(\d+)\s(\d+)\n$')
45 a_command = re.compile(r'^a(\d+)\s(\d+)\n$')
47 def __init__(self, text):
48 """Instantiate and initialize the file content with TEXT."""
50 self._texts = msplit(text)
52 def get_text(self):
53 """Return the current file content."""
55 return "".join(self._texts)
57 def apply_diff(self, diff):
58 """Apply the RCS diff DIFF to the current file content."""
60 ntexts = []
61 ooff = 0
62 diffs = msplit(diff)
63 i = 0
64 while i < len(diffs):
65 admatch = self.ad_command.match(diffs[i])
66 if not admatch:
67 raise MalformedDeltaException('Bad ed command')
68 i += 1
69 sl = int(admatch.group(2))
70 cn = int(admatch.group(3))
71 if admatch.group(1) == 'd': # "d" - Delete command
72 sl -= 1
73 if sl < ooff:
74 raise MalformedDeltaException('Deletion before last edit')
75 if sl > len(self._texts):
76 raise MalformedDeltaException('Deletion past file end')
77 if sl + cn > len(self._texts):
78 raise MalformedDeltaException('Deletion beyond file end')
79 ntexts += self._texts[ooff:sl]
80 ooff = sl + cn
81 else: # "a" - Add command
82 if sl < ooff: # Also catches same place
83 raise MalformedDeltaException('Insertion before last edit')
84 if sl > len(self._texts):
85 raise MalformedDeltaException('Insertion past file end')
86 ntexts += self._texts[ooff:sl] + diffs[i:i + cn]
87 ooff = sl
88 i += cn
89 self._texts = ntexts + self._texts[ooff:]
91 def invert_diff(self, diff):
92 """Apply the RCS diff DIFF to the current file content and simultaneously
93 generate an RCS diff suitable for reverting the change."""
95 ntexts = []
96 ooff = 0
97 diffs = msplit(diff)
98 ndiffs = []
99 adjust = 0
100 i = 0
101 while i < len(diffs):
102 admatch = self.ad_command.match(diffs[i])
103 if not admatch:
104 raise MalformedDeltaException('Bad ed command')
105 i += 1
106 sl = int(admatch.group(2))
107 cn = int(admatch.group(3))
108 if admatch.group(1) == 'd': # "d" - Delete command
109 sl -= 1
110 if sl < ooff:
111 raise MalformedDeltaException('Deletion before last edit')
112 if sl > len(self._texts):
113 raise MalformedDeltaException('Deletion past file end')
114 if sl + cn > len(self._texts):
115 raise MalformedDeltaException('Deletion beyond file end')
116 # Handle substitution explicitly, as add must come after del
117 # (last add may end in no newline, so no command can follow).
118 if i < len(diffs):
119 amatch = self.a_command.match(diffs[i])
120 else:
121 amatch = None
122 if amatch and int(amatch.group(1)) == sl + cn:
123 cn2 = int(amatch.group(2))
124 i += 1
125 ndiffs += ["d%d %d\na%d %d\n" % \
126 (sl + 1 + adjust, cn2, sl + adjust + cn2, cn)] + \
127 self._texts[sl:sl + cn]
128 ntexts += self._texts[ooff:sl] + diffs[i:i + cn2]
129 adjust += cn2 - cn
130 i += cn2
131 else:
132 ndiffs += ["a%d %d\n" % (sl + adjust, cn)] + \
133 self._texts[sl:sl + cn]
134 ntexts += self._texts[ooff:sl]
135 adjust -= cn
136 ooff = sl + cn
137 else: # "a" - Add command
138 if sl < ooff: # Also catches same place
139 raise MalformedDeltaException('Insertion before last edit')
140 if sl > len(self._texts):
141 raise MalformedDeltaException('Insertion past file end')
142 ndiffs += ["d%d %d\n" % (sl + 1 + adjust, cn)]
143 ntexts += self._texts[ooff:sl] + diffs[i:i + cn]
144 ooff = sl
145 adjust += cn
146 i += cn
147 self._texts = ntexts + self._texts[ooff:]
148 return "".join(ndiffs)