| 
									
										
										
										
											1995-04-26 22:57:11 +00:00
										 |  |  | #! /usr/local/bin/python | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """RCS Proxy.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Provide a simplified interface on RCS files, locally or remotely. | 
					
						
							|  |  |  | The functionality is geared towards implementing some sort of | 
					
						
							|  |  |  | remote CVS like utility.  It is modeled after the similar module | 
					
						
							|  |  |  | FSProxy. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The module defines three classes: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RCSProxyLocal  -- used for local access | 
					
						
							|  |  |  | RCSProxyServer -- used on the server side of remote access | 
					
						
							|  |  |  | RCSProxyClient -- used on the client side of remote access | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The remote classes are instantiated with an IP address and an optional | 
					
						
							|  |  |  | verbosity flag. | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import server | 
					
						
							|  |  |  | import client | 
					
						
							|  |  |  | import md5 | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import fnmatch | 
					
						
							|  |  |  | import string | 
					
						
							|  |  |  | import tempfile | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | okchars = string.letters + string.digits + '-_=+.' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RCSProxyLocal: | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def __init__(self): | 
					
						
							|  |  |  | 		self._dirstack = [] | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def _close(self): | 
					
						
							|  |  |  | 		while self._dirstack: | 
					
						
							|  |  |  | 			self.back() | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def pwd(self): | 
					
						
							|  |  |  | 		return os.getcwd() | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def cd(self, name): | 
					
						
							|  |  |  | 		save = os.getcwd() | 
					
						
							|  |  |  | 		os.chdir(name) | 
					
						
							|  |  |  | 		self._dirstack.append(save) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def back(self): | 
					
						
							|  |  |  | 		if not self._dirstack: | 
					
						
							|  |  |  | 			raise os.error, "empty directory stack" | 
					
						
							|  |  |  | 		dir = self._dirstack[-1] | 
					
						
							|  |  |  | 		os.chdir(dir) | 
					
						
							|  |  |  | 		del self._dirstack[-1] | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def _filter(self, files, pat = None): | 
					
						
							|  |  |  | 		if pat: | 
					
						
							|  |  |  | 			def keep(name, pat = pat): | 
					
						
							|  |  |  | 				return fnmatch.fnmatch(name, pat) | 
					
						
							|  |  |  | 			files = filter(keep, files) | 
					
						
							|  |  |  | 		files.sort() | 
					
						
							|  |  |  | 		return files | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def isfile(self, name): | 
					
						
							|  |  |  | 		namev = name + ',v' | 
					
						
							|  |  |  | 		return os.path.isfile(namev) or \ | 
					
						
							|  |  |  | 		       os.path.isfile(os.path.join('RCS', namev)) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def _unmangle(self, name): | 
					
						
							|  |  |  | 		if type(name) == type(''): | 
					
						
							|  |  |  | 			rev = '' | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			name, rev = name | 
					
						
							|  |  |  | 		return name, rev | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def checkfile(self, name): | 
					
						
							|  |  |  | 		name, rev = self._unmangle(name) | 
					
						
							|  |  |  | 		if not self.isfile(name): | 
					
						
							|  |  |  | 			raise os.error, 'not an rcs file %s' % `name` | 
					
						
							|  |  |  | 		for c in rev: | 
					
						
							|  |  |  | 			if c not in okchars: | 
					
						
							|  |  |  | 				raise ValueError, "bad char in rev" | 
					
						
							|  |  |  | 		return name, rev | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def listfiles(self, pat = None): | 
					
						
							|  |  |  | 		def isrcs(name): return name[-2:] == ',v' | 
					
						
							|  |  |  | 		def striprcs(name): return name[:-2] | 
					
						
							|  |  |  | 		files = os.listdir(os.curdir) | 
					
						
							|  |  |  | 		files = filter(isrcs, files) | 
					
						
							|  |  |  | 		if os.path.isdir('RCS'): | 
					
						
							|  |  |  | 			files2 = os.listdir('RCS') | 
					
						
							|  |  |  | 			files2 = filter(isrcs, files2) | 
					
						
							|  |  |  | 			files = files + files2 | 
					
						
							|  |  |  | 		files = map(striprcs, files) | 
					
						
							|  |  |  | 		return self._filter(files, pat) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def listsubdirs(self, pat = None): | 
					
						
							|  |  |  | 		files = os.listdir(os.curdir) | 
					
						
							|  |  |  | 		files = filter(os.path.isdir, files) | 
					
						
							|  |  |  | 		return self._filter(files, pat) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def isdir(self, name): | 
					
						
							|  |  |  | 		return os.path.isdir(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def _open(self, name, cmd = 'co -p'): | 
					
						
							|  |  |  | 		name, rev = self.checkfile(name) | 
					
						
							|  |  |  | 		namev = name + ',v' | 
					
						
							|  |  |  | 		if rev: | 
					
						
							|  |  |  | 			cmd = cmd + ' -r' + rev | 
					
						
							|  |  |  | 		return os.popen('%s %s' %  (cmd, `namev`)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def _closepipe(self, f): | 
					
						
							|  |  |  | 		sts = f.close() | 
					
						
							|  |  |  | 		if sts: | 
					
						
							|  |  |  | 			raise IOError, "Exit status %d" % sts | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def _remove(self, fn): | 
					
						
							|  |  |  | 		try: | 
					
						
							|  |  |  | 			os.unlink(fn) | 
					
						
							|  |  |  | 		except os.error: | 
					
						
							|  |  |  | 			pass | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def sum(self, name): | 
					
						
							|  |  |  | 		f = self._open(name) | 
					
						
							|  |  |  | 		BUFFERSIZE = 1024*8 | 
					
						
							|  |  |  | 		sum = md5.new() | 
					
						
							|  |  |  | 		while 1: | 
					
						
							|  |  |  | 			buffer = f.read(BUFFERSIZE) | 
					
						
							|  |  |  | 			if not buffer: | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			sum.update(buffer) | 
					
						
							|  |  |  | 		self._closepipe(f) | 
					
						
							|  |  |  | 		return sum.digest() | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def _list(self, function, list): | 
					
						
							|  |  |  | 		if list is None: | 
					
						
							|  |  |  | 			list = self.listfiles() | 
					
						
							|  |  |  | 		res = [] | 
					
						
							|  |  |  | 		for name in list: | 
					
						
							|  |  |  | 			try: | 
					
						
							|  |  |  | 				res.append((name, function(name))) | 
					
						
							|  |  |  | 			except (os.error, IOError): | 
					
						
							|  |  |  | 				res.append((name, None)) | 
					
						
							|  |  |  | 		return res | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def sumlist(self, list = None): | 
					
						
							|  |  |  | 		return self.list(self.sum, list) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def _dict(self, function, list): | 
					
						
							|  |  |  | 		if list is None: | 
					
						
							|  |  |  | 			list = self.listfiles() | 
					
						
							|  |  |  | 		dict = {} | 
					
						
							|  |  |  | 		for name in list: | 
					
						
							|  |  |  | 			try: | 
					
						
							|  |  |  | 				dict[name] = function(name) | 
					
						
							|  |  |  | 			except (os.error, IOError): | 
					
						
							|  |  |  | 				pass | 
					
						
							|  |  |  | 		return dict | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def sumdict(self, list = None): | 
					
						
							|  |  |  | 		return self.dict(self.sum, list) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def get(self, name): | 
					
						
							|  |  |  | 		f = self._open(name) | 
					
						
							|  |  |  | 		data = f.read() | 
					
						
							|  |  |  | 		self._closepipe(f) | 
					
						
							|  |  |  | 		return data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def info(self, name): | 
					
						
							|  |  |  | 		f = self._open(name, 'rlog -h') | 
					
						
							|  |  |  | 		dict = {} | 
					
						
							|  |  |  | 		while 1: | 
					
						
							|  |  |  | 			line = f.readline() | 
					
						
							|  |  |  | 			if not line: break | 
					
						
							|  |  |  | 			if line[0] == '\t': | 
					
						
							|  |  |  | 				continue # XXX lock details, later | 
					
						
							|  |  |  | 			i = string.find(line, ':') | 
					
						
							|  |  |  | 			if i > 0: | 
					
						
							|  |  |  | 				key, value = line[:i], string.strip(line[i+1:]) | 
					
						
							|  |  |  | 				dict[key] = value | 
					
						
							|  |  |  | 		self._closepipe(f) | 
					
						
							|  |  |  | 		return dict | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def head(self, name): | 
					
						
							|  |  |  | 		dict = self.info(name) | 
					
						
							|  |  |  | 		return dict['head'] | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def log(self, name, flags = ''): | 
					
						
							|  |  |  | 		f = self._open(name, 'rlog %s 2>&1' % flags) | 
					
						
							|  |  |  | 		log = f.read() | 
					
						
							|  |  |  | 		self._closepipe(f) | 
					
						
							|  |  |  | 		return log | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def put(self, fullname, data, message = ""): | 
					
						
							|  |  |  | 		if message and message[-1] != '\n': | 
					
						
							|  |  |  | 			message = message + '\n' | 
					
						
							|  |  |  | 		name, rev = self._unmangle(fullname) | 
					
						
							|  |  |  | 		new = not self.isfile(name) | 
					
						
							|  |  |  | 		if new: | 
					
						
							|  |  |  | 			for c in name: | 
					
						
							|  |  |  | 				if c not in okchars: | 
					
						
							|  |  |  | 					raise ValueError, "bad char in name" | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			self._remove(name) | 
					
						
							|  |  |  | 		f = open(name, 'w') | 
					
						
							|  |  |  | 		f.write(data) | 
					
						
							|  |  |  | 		f.close() | 
					
						
							|  |  |  | 		tf = tempfile.mktemp() | 
					
						
							|  |  |  | 		try: | 
					
						
							|  |  |  | 			if not new: | 
					
						
							|  |  |  | 			    cmd = "rcs -l%s %s >>%s 2>&1" % (rev, name, tf) | 
					
						
							|  |  |  | 			    sts = os.system(cmd) | 
					
						
							|  |  |  | 			    if sts: | 
					
						
							|  |  |  | 				raise IOError, "rcs -l exit status %d" % sts | 
					
						
							|  |  |  | 			cmd = "ci -r%s %s >>%s 2>&1" % (rev, name, tf) | 
					
						
							|  |  |  | 			p = os.popen(cmd, 'w') | 
					
						
							|  |  |  | 			p.write(message) | 
					
						
							|  |  |  | 			sts = p.close() | 
					
						
							|  |  |  | 			if sts: | 
					
						
							|  |  |  | 				raise IOError, "ci exit status %d" % sts | 
					
						
							|  |  |  | 			messages = open(tf).read() | 
					
						
							|  |  |  | 			return messages or None | 
					
						
							|  |  |  | 		finally: | 
					
						
							|  |  |  | 			self._remove(tf) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def mkdir(self, name): | 
					
						
							|  |  |  | 		os.mkdir(name, 0777) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def rmdir(self, name): | 
					
						
							|  |  |  | 		os.rmdir(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1995-06-21 02:09:20 +00:00
										 |  |  | class RCSProxyServer(RCSProxyLocal, server.SecureServer): | 
					
						
							| 
									
										
										
										
											1995-04-26 22:57:11 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	def __init__(self, address, verbose = server.VERBOSE): | 
					
						
							|  |  |  | 		RCSProxyLocal.__init__(self) | 
					
						
							| 
									
										
										
										
											1995-06-21 02:09:20 +00:00
										 |  |  | 		server.SecureServer.__init__(self, address, verbose) | 
					
						
							| 
									
										
										
										
											1995-04-26 22:57:11 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	def _close(self): | 
					
						
							| 
									
										
										
										
											1995-06-21 02:09:20 +00:00
										 |  |  | 		server.SecureServer._close(self) | 
					
						
							| 
									
										
										
										
											1995-04-26 22:57:11 +00:00
										 |  |  | 		RCSProxyLocal._close(self) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def _serve(self): | 
					
						
							| 
									
										
										
										
											1995-06-21 02:09:20 +00:00
										 |  |  | 		server.SecureServer._serve(self) | 
					
						
							| 
									
										
										
										
											1995-04-26 22:57:11 +00:00
										 |  |  | 		# Retreat into start directory | 
					
						
							|  |  |  | 		while self._dirstack: self.back() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1995-06-21 02:09:20 +00:00
										 |  |  | class RCSProxyClient(client.SecureClient): | 
					
						
							| 
									
										
										
										
											1995-04-26 22:57:11 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	def __init__(self, address, verbose = client.VERBOSE): | 
					
						
							| 
									
										
										
										
											1995-06-21 02:09:20 +00:00
										 |  |  | 		client.SecureClient.__init__(self, address, verbose) | 
					
						
							| 
									
										
										
										
											1995-04-26 22:57:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_server(): | 
					
						
							|  |  |  | 	import string | 
					
						
							|  |  |  | 	import sys | 
					
						
							|  |  |  | 	if sys.argv[1:]: | 
					
						
							|  |  |  | 		port = string.atoi(sys.argv[1]) | 
					
						
							|  |  |  | 	else: | 
					
						
							|  |  |  | 		port = 4127 | 
					
						
							|  |  |  | 	proxy = RCSProxyServer(('', port)) | 
					
						
							|  |  |  | 	proxy._serverloop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test(): | 
					
						
							|  |  |  | 	import sys | 
					
						
							|  |  |  | 	if not sys.argv[1:] or sys.argv[1] and sys.argv[1][0] in '0123456789': | 
					
						
							|  |  |  | 		test_server() | 
					
						
							|  |  |  | 		sys.exit(0) | 
					
						
							|  |  |  | 	proxy = RCSProxyLocal() | 
					
						
							|  |  |  | 	what = sys.argv[1] | 
					
						
							|  |  |  | 	if hasattr(proxy, what): | 
					
						
							|  |  |  | 		attr = getattr(proxy, what) | 
					
						
							|  |  |  | 		if callable(attr): | 
					
						
							|  |  |  | 			print apply(attr, tuple(sys.argv[2:])) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			print `attr` | 
					
						
							|  |  |  | 	else: | 
					
						
							|  |  |  | 		print "%s: no such attribute" % what | 
					
						
							|  |  |  | 		sys.exit(2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  | 	test() |