blob: 0a12157d8dc4672d2697cd22c819bd7b4338971c [file] [log] [blame]
Guido van Rossum5f07b841995-04-26 22:57:11 +00001#! /usr/local/bin/python
2
3"""RCS Proxy.
4
5Provide a simplified interface on RCS files, locally or remotely.
6The functionality is geared towards implementing some sort of
7remote CVS like utility. It is modeled after the similar module
8FSProxy.
9
10The module defines three classes:
11
12RCSProxyLocal -- used for local access
13RCSProxyServer -- used on the server side of remote access
14RCSProxyClient -- used on the client side of remote access
15
16The remote classes are instantiated with an IP address and an optional
17verbosity flag.
18"""
19
20import server
21import client
22import md5
23import os
24import fnmatch
25import string
26import tempfile
27
28
29okchars = string.letters + string.digits + '-_=+.'
30
31
32class RCSProxyLocal:
33
34 def __init__(self):
35 self._dirstack = []
36
37 def _close(self):
38 while self._dirstack:
39 self.back()
40
41 def pwd(self):
42 return os.getcwd()
43
44 def cd(self, name):
45 save = os.getcwd()
46 os.chdir(name)
47 self._dirstack.append(save)
48
49 def back(self):
50 if not self._dirstack:
51 raise os.error, "empty directory stack"
52 dir = self._dirstack[-1]
53 os.chdir(dir)
54 del self._dirstack[-1]
55
56 def _filter(self, files, pat = None):
57 if pat:
58 def keep(name, pat = pat):
59 return fnmatch.fnmatch(name, pat)
60 files = filter(keep, files)
61 files.sort()
62 return files
63
64 def isfile(self, name):
65 namev = name + ',v'
66 return os.path.isfile(namev) or \
67 os.path.isfile(os.path.join('RCS', namev))
68
69 def _unmangle(self, name):
70 if type(name) == type(''):
71 rev = ''
72 else:
73 name, rev = name
74 return name, rev
75
76 def checkfile(self, name):
77 name, rev = self._unmangle(name)
78 if not self.isfile(name):
79 raise os.error, 'not an rcs file %s' % `name`
80 for c in rev:
81 if c not in okchars:
82 raise ValueError, "bad char in rev"
83 return name, rev
84
85 def listfiles(self, pat = None):
86 def isrcs(name): return name[-2:] == ',v'
87 def striprcs(name): return name[:-2]
88 files = os.listdir(os.curdir)
89 files = filter(isrcs, files)
90 if os.path.isdir('RCS'):
91 files2 = os.listdir('RCS')
92 files2 = filter(isrcs, files2)
93 files = files + files2
94 files = map(striprcs, files)
95 return self._filter(files, pat)
96
97 def listsubdirs(self, pat = None):
98 files = os.listdir(os.curdir)
99 files = filter(os.path.isdir, files)
100 return self._filter(files, pat)
101
102 def isdir(self, name):
103 return os.path.isdir(name)
104
105 def _open(self, name, cmd = 'co -p'):
106 name, rev = self.checkfile(name)
107 namev = name + ',v'
108 if rev:
109 cmd = cmd + ' -r' + rev
110 return os.popen('%s %s' % (cmd, `namev`))
111
112 def _closepipe(self, f):
113 sts = f.close()
114 if sts:
115 raise IOError, "Exit status %d" % sts
116
117 def _remove(self, fn):
118 try:
119 os.unlink(fn)
120 except os.error:
121 pass
122
123 def sum(self, name):
124 f = self._open(name)
125 BUFFERSIZE = 1024*8
126 sum = md5.new()
127 while 1:
128 buffer = f.read(BUFFERSIZE)
129 if not buffer:
130 break
131 sum.update(buffer)
132 self._closepipe(f)
133 return sum.digest()
134
135 def _list(self, function, list):
136 if list is None:
137 list = self.listfiles()
138 res = []
139 for name in list:
140 try:
141 res.append((name, function(name)))
142 except (os.error, IOError):
143 res.append((name, None))
144 return res
145
146 def sumlist(self, list = None):
147 return self.list(self.sum, list)
148
149 def _dict(self, function, list):
150 if list is None:
151 list = self.listfiles()
152 dict = {}
153 for name in list:
154 try:
155 dict[name] = function(name)
156 except (os.error, IOError):
157 pass
158 return dict
159
160 def sumdict(self, list = None):
161 return self.dict(self.sum, list)
162
163 def get(self, name):
164 f = self._open(name)
165 data = f.read()
166 self._closepipe(f)
167 return data
168
169 def info(self, name):
170 f = self._open(name, 'rlog -h')
171 dict = {}
172 while 1:
173 line = f.readline()
174 if not line: break
175 if line[0] == '\t':
176 continue # XXX lock details, later
177 i = string.find(line, ':')
178 if i > 0:
179 key, value = line[:i], string.strip(line[i+1:])
180 dict[key] = value
181 self._closepipe(f)
182 return dict
183
184 def head(self, name):
185 dict = self.info(name)
186 return dict['head']
187
188 def log(self, name, flags = ''):
189 f = self._open(name, 'rlog %s 2>&1' % flags)
190 log = f.read()
191 self._closepipe(f)
192 return log
193
194 def put(self, fullname, data, message = ""):
195 if message and message[-1] != '\n':
196 message = message + '\n'
197 name, rev = self._unmangle(fullname)
198 new = not self.isfile(name)
199 if new:
200 for c in name:
201 if c not in okchars:
202 raise ValueError, "bad char in name"
203 else:
204 self._remove(name)
205 f = open(name, 'w')
206 f.write(data)
207 f.close()
208 tf = tempfile.mktemp()
209 try:
210 if not new:
211 cmd = "rcs -l%s %s >>%s 2>&1" % (rev, name, tf)
212 sts = os.system(cmd)
213 if sts:
214 raise IOError, "rcs -l exit status %d" % sts
215 cmd = "ci -r%s %s >>%s 2>&1" % (rev, name, tf)
216 p = os.popen(cmd, 'w')
217 p.write(message)
218 sts = p.close()
219 if sts:
220 raise IOError, "ci exit status %d" % sts
221 messages = open(tf).read()
222 return messages or None
223 finally:
224 self._remove(tf)
225
226 def mkdir(self, name):
227 os.mkdir(name, 0777)
228
229 def rmdir(self, name):
230 os.rmdir(name)
231
232
233class RCSProxyServer(RCSProxyLocal, server.Server):
234
235 def __init__(self, address, verbose = server.VERBOSE):
236 RCSProxyLocal.__init__(self)
237 server.Server.__init__(self, address, verbose)
238
239 def _close(self):
240 server.Server._close(self)
241 RCSProxyLocal._close(self)
242
243 def _serve(self):
244 server.Server._serve(self)
245 # Retreat into start directory
246 while self._dirstack: self.back()
247
248
249class RCSProxyClient(client.Client):
250
251 def __init__(self, address, verbose = client.VERBOSE):
252 client.Client.__init__(self, address, verbose)
253
254
255def test_server():
256 import string
257 import sys
258 if sys.argv[1:]:
259 port = string.atoi(sys.argv[1])
260 else:
261 port = 4127
262 proxy = RCSProxyServer(('', port))
263 proxy._serverloop()
264
265
266def test():
267 import sys
268 if not sys.argv[1:] or sys.argv[1] and sys.argv[1][0] in '0123456789':
269 test_server()
270 sys.exit(0)
271 proxy = RCSProxyLocal()
272 what = sys.argv[1]
273 if hasattr(proxy, what):
274 attr = getattr(proxy, what)
275 if callable(attr):
276 print apply(attr, tuple(sys.argv[2:]))
277 else:
278 print `attr`
279 else:
280 print "%s: no such attribute" % what
281 sys.exit(2)
282
283
284if __name__ == '__main__':
285 test()