Document how various VCSs handle keywords, EOLs, and file permissions.
[cvs2svn.git] / contrib / rcs_file_filter.py
blob1be3b3f2d3b9e61ff637b3bd3fedb92ad35c0812
1 #! /usr/bin/python
3 # (Be in -*- python -*- mode.)
5 # ====================================================================
6 # Copyright (c) 2006-2008 CollabNet. All rights reserved.
8 # This software is licensed as described in the file COPYING, which
9 # you should have received as part of this distribution. The terms
10 # are also available at http://subversion.tigris.org/license-1.html.
11 # If newer versions of this license are posted there, you may use a
12 # newer version instead, at your option.
14 # This software consists of voluntary contributions made by many
15 # individuals. For exact contribution history, see the revision
16 # history and logs, available at http://cvs2svn.tigris.org/.
17 # ====================================================================
19 """Filter an RCS file."""
21 import sys
22 import os
23 import time
25 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
27 import cvs2svn_rcsparse
30 def at_quote(s):
31 return '@' + s.replace('@', '@@') + '@'
34 def format_date(date):
35 date_tuple = time.gmtime(date)
36 year = date_tuple[0]
37 if 1900 <= year <= 1999:
38 year = '%02d' % (year - 1900)
39 else:
40 year = '%04d' % year
41 return year + time.strftime('.%m.%d.%H.%M.%S', date_tuple)
44 class WriteRCSFileSink(cvs2svn_rcsparse.Sink):
45 """A Sink that outputs reconstructed RCS file contents."""
47 def __init__(self, f):
48 """Create a Sink object that will write its output into F.
50 F should be a file-like object."""
52 self.f = f
53 self.head = None
54 self.principal_branch = None
55 self.accessors = []
56 self.symbols = []
57 self.lockers = []
58 self.locking = None
59 self.comment = None
60 self.expansion = None
62 def set_head_revision(self, revision):
63 self.head = revision
65 def set_principal_branch(self, branch_name):
66 self.principal_branch = branch_name
68 def set_access(self, accessors):
69 self.accessors = accessors
71 def define_tag(self, name, revision):
72 self.symbols.append((name, revision,))
74 def set_locker(self, revision, locker):
75 self.lockers.append((revision, locker,))
77 def set_locking(self, mode):
78 self.locking = mode
80 def set_comment(self, comment):
81 self.comment = comment
83 def set_expansion(self, mode):
84 self.expansion = mode
86 def admin_completed(self):
87 self.f.write('head\t%s;\n' % self.head)
88 if self.principal_branch is not None:
89 self.f.write('branch\t%s;\n' % self.principal_branch)
90 self.f.write('access')
91 for accessor in self.accessors:
92 self.f.write('\n\t%s' % accessor)
93 self.f.write(';\n')
94 self.f.write('symbols')
95 for (name, revision) in self.symbols:
96 self.f.write('\n\t%s:%s' % (name, revision))
97 self.f.write(';\n')
98 self.f.write('locks')
99 for (revision, locker) in self.lockers:
100 self.f.write('\n\t%s:%s' % (locker, revision))
101 self.f.write(';')
102 if self.locking is not None:
103 self.f.write(' %s;' % self.locking)
104 self.f.write('\n')
105 if self.comment is not None:
106 self.f.write('comment\t%s;\n' % at_quote(self.comment))
107 if self.expansion is not None:
108 self.f.write('expand\t%s;\n' % at_quote(self.expansion))
109 self.f.write('\n')
111 def define_revision(
112 self, revision, timestamp, author, state, branches, next
114 self.f.write(
115 '\n%s\ndate\t%s;\tauthor %s;\tstate %s;\n'
116 % (revision, format_date(timestamp), author, state,)
118 self.f.write('branches')
119 for branch in branches:
120 self.f.write('\n\t%s' % branch)
121 self.f.write(';\n')
122 self.f.write('next\t%s;\n' % (next or ''))
124 def tree_completed(self):
125 pass
127 def set_description(self, description):
128 self.f.write('\n\ndesc\n%s\n' % at_quote(description))
130 def set_revision_info(self, revision, log, text):
131 self.f.write('\n')
132 self.f.write('\n')
133 self.f.write('%s\n' % revision)
134 self.f.write('log\n%s\n' % at_quote(log))
135 self.f.write('text\n%s\n' % at_quote(text))
137 def parse_completed(self):
138 pass
141 class FilterSink(cvs2svn_rcsparse.Sink):
142 """A Sink that passes callbacks through to another sink.
144 This is intended for use as a base class for other filter classes
145 that modify the data before passing it through."""
147 def __init__(self, sink):
148 """Create a Sink object that will write its output into SINK.
150 SINK should be a cvs2svn_rcsparse.Sink."""
152 self.sink = sink
154 def set_head_revision(self, revision):
155 self.sink.set_head_revision(revision)
157 def set_principal_branch(self, branch_name):
158 self.sink.set_principal_branch(branch_name)
160 def set_access(self, accessors):
161 self.sink.set_access(accessors)
163 def define_tag(self, name, revision):
164 self.sink.define_tag(name, revision)
166 def set_locker(self, revision, locker):
167 self.sink.set_locker(revision, locker)
169 def set_locking(self, mode):
170 self.sink.set_locking(mode)
172 def set_comment(self, comment):
173 self.sink.set_comment(comment)
175 def set_expansion(self, mode):
176 self.sink.set_expansion(mode)
178 def admin_completed(self):
179 self.sink.admin_completed()
181 def define_revision(
182 self, revision, timestamp, author, state, branches, next
184 self.sink.define_revision(
185 revision, timestamp, author, state, branches, next
188 def tree_completed(self):
189 self.sink.tree_completed()
191 def set_description(self, description):
192 self.sink.set_description(description)
194 def set_revision_info(self, revision, log, text):
195 self.sink.set_revision_info(revision, log, text)
197 def parse_completed(self):
198 self.sink.parse_completed()
201 if __name__ == '__main__':
202 if sys.argv[1:]:
203 for path in sys.argv[1:]:
204 if os.path.isfile(path) and path.endswith(',v'):
205 cvs2svn_rcsparse.parse(
206 open(path, 'rb'), WriteRCSFileSink(sys.stdout)
208 else:
209 sys.stderr.write('%r is being ignored.\n' % path)
210 else:
211 cvs2svn_rcsparse.parse(sys.stdin, WriteRCSFileSink(sys.stdout))