Remove obsolete pdist demo.
This commit is contained in:
parent
2db2b8a17d
commit
68642f5ed0
|
@ -35,9 +35,6 @@ newmetaclasses Demonstration of metaclasses.
|
||||||
|
|
||||||
parser Example using the parser module.
|
parser Example using the parser module.
|
||||||
|
|
||||||
pdist Old, unfinished code messing with CVS, RCS and remote
|
|
||||||
files.
|
|
||||||
|
|
||||||
pysvr An example of embedding Python in a threaded
|
pysvr An example of embedding Python in a threaded
|
||||||
application.
|
application.
|
||||||
|
|
||||||
|
|
|
@ -1,301 +0,0 @@
|
||||||
"""File System Proxy.
|
|
||||||
|
|
||||||
Provide an OS-neutral view on a file system, locally or remotely.
|
|
||||||
The functionality is geared towards implementing some sort of
|
|
||||||
rdist-like utility between a Mac and a UNIX system.
|
|
||||||
|
|
||||||
The module defines three classes:
|
|
||||||
|
|
||||||
FSProxyLocal -- used for local access
|
|
||||||
FSProxyServer -- used on the server side of remote access
|
|
||||||
FSProxyClient -- 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
|
|
||||||
from stat import *
|
|
||||||
import time
|
|
||||||
import fnmatch
|
|
||||||
|
|
||||||
maxnamelen = 255
|
|
||||||
|
|
||||||
skipnames = (os.curdir, os.pardir)
|
|
||||||
|
|
||||||
|
|
||||||
class FSProxyLocal:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._dirstack = []
|
|
||||||
self._ignore = ['*.pyc'] + self._readignore()
|
|
||||||
|
|
||||||
def _close(self):
|
|
||||||
while self._dirstack:
|
|
||||||
self.back()
|
|
||||||
|
|
||||||
def _readignore(self):
|
|
||||||
file = self._hide('ignore')
|
|
||||||
try:
|
|
||||||
f = open(file)
|
|
||||||
except IOError:
|
|
||||||
file = self._hide('synctree.ignorefiles')
|
|
||||||
try:
|
|
||||||
f = open(file)
|
|
||||||
except IOError:
|
|
||||||
return []
|
|
||||||
ignore = []
|
|
||||||
while 1:
|
|
||||||
line = f.readline()
|
|
||||||
if not line: break
|
|
||||||
if line[-1] == '\n': line = line[:-1]
|
|
||||||
ignore.append(line)
|
|
||||||
f.close()
|
|
||||||
return ignore
|
|
||||||
|
|
||||||
def _hidden(self, name):
|
|
||||||
return name[0] == '.'
|
|
||||||
|
|
||||||
def _hide(self, name):
|
|
||||||
return '.%s' % name
|
|
||||||
|
|
||||||
def visible(self, name):
|
|
||||||
if len(name) > maxnamelen: return 0
|
|
||||||
if name[-1] == '~': return 0
|
|
||||||
if name in skipnames: return 0
|
|
||||||
if self._hidden(name): return 0
|
|
||||||
head, tail = os.path.split(name)
|
|
||||||
if head or not tail: return 0
|
|
||||||
if os.path.islink(name): return 0
|
|
||||||
if '\0' in open(name, 'rb').read(512): return 0
|
|
||||||
for ign in self._ignore:
|
|
||||||
if fnmatch.fnmatch(name, ign): return 0
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def check(self, name):
|
|
||||||
if not self.visible(name):
|
|
||||||
raise os.error("protected name %s" % repr(name))
|
|
||||||
|
|
||||||
def checkfile(self, name):
|
|
||||||
self.check(name)
|
|
||||||
if not os.path.isfile(name):
|
|
||||||
raise os.error("not a plain file %s" % repr(name))
|
|
||||||
|
|
||||||
def pwd(self):
|
|
||||||
return os.getcwd()
|
|
||||||
|
|
||||||
def cd(self, name):
|
|
||||||
self.check(name)
|
|
||||||
save = os.getcwd(), self._ignore
|
|
||||||
os.chdir(name)
|
|
||||||
self._dirstack.append(save)
|
|
||||||
self._ignore = self._ignore + self._readignore()
|
|
||||||
|
|
||||||
def back(self):
|
|
||||||
if not self._dirstack:
|
|
||||||
raise os.error("empty directory stack")
|
|
||||||
dir, ignore = self._dirstack[-1]
|
|
||||||
os.chdir(dir)
|
|
||||||
del self._dirstack[-1]
|
|
||||||
self._ignore = ignore
|
|
||||||
|
|
||||||
def _filter(self, files, pat = None):
|
|
||||||
if pat:
|
|
||||||
def keep(name, pat = pat):
|
|
||||||
return fnmatch.fnmatch(name, pat)
|
|
||||||
files = list(filter(keep, files))
|
|
||||||
files = list(filter(self.visible, files))
|
|
||||||
files.sort()
|
|
||||||
return files
|
|
||||||
|
|
||||||
def list(self, pat = None):
|
|
||||||
files = os.listdir(os.curdir)
|
|
||||||
return self._filter(files, pat)
|
|
||||||
|
|
||||||
def listfiles(self, pat = None):
|
|
||||||
files = os.listdir(os.curdir)
|
|
||||||
files = list(filter(os.path.isfile, files))
|
|
||||||
return self._filter(files, pat)
|
|
||||||
|
|
||||||
def listsubdirs(self, pat = None):
|
|
||||||
files = os.listdir(os.curdir)
|
|
||||||
files = list(filter(os.path.isdir, files))
|
|
||||||
return self._filter(files, pat)
|
|
||||||
|
|
||||||
def exists(self, name):
|
|
||||||
return self.visible(name) and os.path.exists(name)
|
|
||||||
|
|
||||||
def isdir(self, name):
|
|
||||||
return self.visible(name) and os.path.isdir(name)
|
|
||||||
|
|
||||||
def islink(self, name):
|
|
||||||
return self.visible(name) and os.path.islink(name)
|
|
||||||
|
|
||||||
def isfile(self, name):
|
|
||||||
return self.visible(name) and os.path.isfile(name)
|
|
||||||
|
|
||||||
def sum(self, name):
|
|
||||||
self.checkfile(name)
|
|
||||||
BUFFERSIZE = 1024*8
|
|
||||||
f = open(name)
|
|
||||||
sum = md5.new()
|
|
||||||
while 1:
|
|
||||||
buffer = f.read(BUFFERSIZE)
|
|
||||||
if not buffer:
|
|
||||||
break
|
|
||||||
sum.update(buffer)
|
|
||||||
return sum.digest()
|
|
||||||
|
|
||||||
def size(self, name):
|
|
||||||
self.checkfile(name)
|
|
||||||
return os.stat(name)[ST_SIZE]
|
|
||||||
|
|
||||||
def mtime(self, name):
|
|
||||||
self.checkfile(name)
|
|
||||||
return time.localtime(os.stat(name)[ST_MTIME])
|
|
||||||
|
|
||||||
def stat(self, name):
|
|
||||||
self.checkfile(name)
|
|
||||||
size = os.stat(name)[ST_SIZE]
|
|
||||||
mtime = time.localtime(os.stat(name)[ST_MTIME])
|
|
||||||
return size, mtime
|
|
||||||
|
|
||||||
def info(self, name):
|
|
||||||
sum = self.sum(name)
|
|
||||||
size = os.stat(name)[ST_SIZE]
|
|
||||||
mtime = time.localtime(os.stat(name)[ST_MTIME])
|
|
||||||
return sum, size, mtime
|
|
||||||
|
|
||||||
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 statlist(self, list = None):
|
|
||||||
return self._list(self.stat, list)
|
|
||||||
|
|
||||||
def mtimelist(self, list = None):
|
|
||||||
return self._list(self.mtime, list)
|
|
||||||
|
|
||||||
def sizelist(self, list = None):
|
|
||||||
return self._list(self.size, list)
|
|
||||||
|
|
||||||
def infolist(self, list = None):
|
|
||||||
return self._list(self.info, 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 sizedict(self, list = None):
|
|
||||||
return self.dict(self.size, list)
|
|
||||||
|
|
||||||
def mtimedict(self, list = None):
|
|
||||||
return self.dict(self.mtime, list)
|
|
||||||
|
|
||||||
def statdict(self, list = None):
|
|
||||||
return self.dict(self.stat, list)
|
|
||||||
|
|
||||||
def infodict(self, list = None):
|
|
||||||
return self._dict(self.info, list)
|
|
||||||
|
|
||||||
def read(self, name, offset = 0, length = -1):
|
|
||||||
self.checkfile(name)
|
|
||||||
f = open(name)
|
|
||||||
f.seek(offset)
|
|
||||||
if length == 0:
|
|
||||||
data = ''
|
|
||||||
elif length < 0:
|
|
||||||
data = f.read()
|
|
||||||
else:
|
|
||||||
data = f.read(length)
|
|
||||||
f.close()
|
|
||||||
return data
|
|
||||||
|
|
||||||
def create(self, name):
|
|
||||||
self.check(name)
|
|
||||||
if os.path.exists(name):
|
|
||||||
self.checkfile(name)
|
|
||||||
bname = name + '~'
|
|
||||||
try:
|
|
||||||
os.unlink(bname)
|
|
||||||
except os.error:
|
|
||||||
pass
|
|
||||||
os.rename(name, bname)
|
|
||||||
f = open(name, 'w')
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def write(self, name, data, offset = 0):
|
|
||||||
self.checkfile(name)
|
|
||||||
f = open(name, 'r+')
|
|
||||||
f.seek(offset)
|
|
||||||
f.write(data)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def mkdir(self, name):
|
|
||||||
self.check(name)
|
|
||||||
os.mkdir(name, 0o777)
|
|
||||||
|
|
||||||
def rmdir(self, name):
|
|
||||||
self.check(name)
|
|
||||||
os.rmdir(name)
|
|
||||||
|
|
||||||
|
|
||||||
class FSProxyServer(FSProxyLocal, server.Server):
|
|
||||||
|
|
||||||
def __init__(self, address, verbose = server.VERBOSE):
|
|
||||||
FSProxyLocal.__init__(self)
|
|
||||||
server.Server.__init__(self, address, verbose)
|
|
||||||
|
|
||||||
def _close(self):
|
|
||||||
server.Server._close(self)
|
|
||||||
FSProxyLocal._close(self)
|
|
||||||
|
|
||||||
def _serve(self):
|
|
||||||
server.Server._serve(self)
|
|
||||||
# Retreat into start directory
|
|
||||||
while self._dirstack: self.back()
|
|
||||||
|
|
||||||
|
|
||||||
class FSProxyClient(client.Client):
|
|
||||||
|
|
||||||
def __init__(self, address, verbose = client.VERBOSE):
|
|
||||||
client.Client.__init__(self, address, verbose)
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
import string
|
|
||||||
import sys
|
|
||||||
if sys.argv[1:]:
|
|
||||||
port = string.atoi(sys.argv[1])
|
|
||||||
else:
|
|
||||||
port = 4127
|
|
||||||
proxy = FSProxyServer(('', port))
|
|
||||||
proxy._serverloop()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
test()
|
|
|
@ -1,198 +0,0 @@
|
||||||
#! /usr/bin/env python3
|
|
||||||
|
|
||||||
"""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 two classes:
|
|
||||||
|
|
||||||
RCSProxyLocal -- used for local access
|
|
||||||
RCSProxyServer -- used on the server side of remote access
|
|
||||||
|
|
||||||
The corresponding client class, RCSProxyClient, is defined in module
|
|
||||||
rcsclient.
|
|
||||||
|
|
||||||
The remote classes are instantiated with an IP address and an optional
|
|
||||||
verbosity flag.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import server
|
|
||||||
import md5
|
|
||||||
import os
|
|
||||||
import fnmatch
|
|
||||||
import string
|
|
||||||
import tempfile
|
|
||||||
import rcslib
|
|
||||||
|
|
||||||
|
|
||||||
class DirSupport:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._dirstack = []
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self._close()
|
|
||||||
|
|
||||||
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 listsubdirs(self, pat = None):
|
|
||||||
files = os.listdir(os.curdir)
|
|
||||||
files = list(filter(os.path.isdir, files))
|
|
||||||
return self._filter(files, pat)
|
|
||||||
|
|
||||||
def isdir(self, name):
|
|
||||||
return os.path.isdir(name)
|
|
||||||
|
|
||||||
def mkdir(self, name):
|
|
||||||
os.mkdir(name, 0o777)
|
|
||||||
|
|
||||||
def rmdir(self, name):
|
|
||||||
os.rmdir(name)
|
|
||||||
|
|
||||||
|
|
||||||
class RCSProxyLocal(rcslib.RCS, DirSupport):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
rcslib.RCS.__init__(self)
|
|
||||||
DirSupport.__init__(self)
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
DirSupport.__del__(self)
|
|
||||||
rcslib.RCS.__del__(self)
|
|
||||||
|
|
||||||
def sumlist(self, list = None):
|
|
||||||
return self._list(self.sum, list)
|
|
||||||
|
|
||||||
def sumdict(self, list = None):
|
|
||||||
return self._dict(self.sum, list)
|
|
||||||
|
|
||||||
def sum(self, name_rev):
|
|
||||||
f = self._open(name_rev)
|
|
||||||
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 get(self, name_rev):
|
|
||||||
f = self._open(name_rev)
|
|
||||||
data = f.read()
|
|
||||||
self._closepipe(f)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def put(self, name_rev, data, message=None):
|
|
||||||
name, rev = self._unmangle(name_rev)
|
|
||||||
f = open(name, 'w')
|
|
||||||
f.write(data)
|
|
||||||
f.close()
|
|
||||||
self.checkin(name_rev, message)
|
|
||||||
self._remove(name)
|
|
||||||
|
|
||||||
def _list(self, function, list = None):
|
|
||||||
"""INTERNAL: apply FUNCTION to all files in LIST.
|
|
||||||
|
|
||||||
Return a list of the results.
|
|
||||||
|
|
||||||
The list defaults to all files in the directory if None.
|
|
||||||
|
|
||||||
"""
|
|
||||||
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 _dict(self, function, list = None):
|
|
||||||
"""INTERNAL: apply FUNCTION to all files in LIST.
|
|
||||||
|
|
||||||
Return a dictionary mapping files to results.
|
|
||||||
|
|
||||||
The list defaults to all files in the directory if None.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if list is None:
|
|
||||||
list = self.listfiles()
|
|
||||||
dict = {}
|
|
||||||
for name in list:
|
|
||||||
try:
|
|
||||||
dict[name] = function(name)
|
|
||||||
except (os.error, IOError):
|
|
||||||
pass
|
|
||||||
return dict
|
|
||||||
|
|
||||||
|
|
||||||
class RCSProxyServer(RCSProxyLocal, server.SecureServer):
|
|
||||||
|
|
||||||
def __init__(self, address, verbose = server.VERBOSE):
|
|
||||||
RCSProxyLocal.__init__(self)
|
|
||||||
server.SecureServer.__init__(self, address, verbose)
|
|
||||||
|
|
||||||
def _close(self):
|
|
||||||
server.SecureServer._close(self)
|
|
||||||
RCSProxyLocal._close(self)
|
|
||||||
|
|
||||||
def _serve(self):
|
|
||||||
server.SecureServer._serve(self)
|
|
||||||
# Retreat into start directory
|
|
||||||
while self._dirstack: self.back()
|
|
||||||
|
|
||||||
|
|
||||||
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 hasattr(attr, '__call__'):
|
|
||||||
print(attr(*sys.argv[2:]))
|
|
||||||
else:
|
|
||||||
print(repr(attr))
|
|
||||||
else:
|
|
||||||
print("%s: no such attribute" % what)
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
test()
|
|
|
@ -1,121 +0,0 @@
|
||||||
Filesystem, RCS and CVS client and server classes
|
|
||||||
=================================================
|
|
||||||
|
|
||||||
*** See the security warning at the end of this file! ***
|
|
||||||
|
|
||||||
This directory contains various modules and classes that support
|
|
||||||
remote file system operations.
|
|
||||||
|
|
||||||
CVS stuff
|
|
||||||
---------
|
|
||||||
|
|
||||||
rcvs Script to put in your bin directory
|
|
||||||
rcvs.py Remote CVS client command line interface
|
|
||||||
|
|
||||||
cvslib.py CVS admin files classes (used by rrcs)
|
|
||||||
cvslock.py CVS locking algorithms
|
|
||||||
|
|
||||||
RCS stuff
|
|
||||||
---------
|
|
||||||
|
|
||||||
rrcs Script to put in your bin directory
|
|
||||||
rrcs.py Remote RCS client command line interface
|
|
||||||
|
|
||||||
rcsclient.py Return an RCSProxyClient instance
|
|
||||||
(has reasonable default server/port/directory)
|
|
||||||
|
|
||||||
RCSProxy.py RCS proxy and server classes (on top of rcslib.py)
|
|
||||||
|
|
||||||
rcslib.py Local-only RCS base class (affects stdout &
|
|
||||||
local work files)
|
|
||||||
|
|
||||||
FSProxy stuff
|
|
||||||
-------------
|
|
||||||
|
|
||||||
sumtree.py Old demo for FSProxy
|
|
||||||
cmptree.py First FSProxy client (used to sync from the Mac)
|
|
||||||
FSProxy.py Filesystem interface classes
|
|
||||||
|
|
||||||
Generic client/server stuff
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
client.py Client class
|
|
||||||
server.py Server class
|
|
||||||
|
|
||||||
security.py Security mix-in class (not very secure I think)
|
|
||||||
|
|
||||||
Other generic stuff
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
cmdfw.py CommandFrameWork class
|
|
||||||
(used by rcvs, should be used by rrcs as well)
|
|
||||||
|
|
||||||
|
|
||||||
Client/Server operation
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
The Client and Server classes implement a simple-minded RPC protocol,
|
|
||||||
using Python's pickle module to transfer arguments, return values and
|
|
||||||
exceptions with the most generality. The Server class is instantiated
|
|
||||||
with a port number on which it should listen for requests; the Client
|
|
||||||
class is instantiated with a host name and a port number where it
|
|
||||||
should connect to. Once a client is connected, a TCP connection is
|
|
||||||
maintained between client and server.
|
|
||||||
|
|
||||||
The Server class currently handles only one connection at a time;
|
|
||||||
however it could be rewritten to allow various modes of operations,
|
|
||||||
using multiple threads or processes or the select() system call as
|
|
||||||
desired to serve multiple clients simultaneously (when using select(),
|
|
||||||
still handling one request at a time). This would not require
|
|
||||||
rewriting of the Client class. It may also be possible to adapt the
|
|
||||||
code to use UDP instead of TCP, but then both classes will have to be
|
|
||||||
rewritten (and unless extensive acknowlegements and request serial
|
|
||||||
numbers are used, the server should handle duplicate requests, so its
|
|
||||||
semantics should be idempotent -- shrudder).
|
|
||||||
|
|
||||||
Even though the FSProxy and RCSProxy modules define client classes,
|
|
||||||
the client class is fully generic -- what methods it supports is
|
|
||||||
determined entirely by the server. The server class, however, must be
|
|
||||||
derived from. This is generally done as follows:
|
|
||||||
|
|
||||||
from server import Server
|
|
||||||
from client import Client
|
|
||||||
|
|
||||||
# Define a class that performs the operations locally
|
|
||||||
class MyClassLocal:
|
|
||||||
def __init__(self): ...
|
|
||||||
def _close(self): ...
|
|
||||||
|
|
||||||
# Derive a server class using multiple inheritance
|
|
||||||
class MyClassServer(MyClassLocal, Server):
|
|
||||||
def __init__(self, address):
|
|
||||||
# Must initialize MyClassLocal as well as Server
|
|
||||||
MyClassLocal.__init__(self)
|
|
||||||
Server.__init__(self, address)
|
|
||||||
def _close(self):
|
|
||||||
Server._close()
|
|
||||||
MyClassLocal._close()
|
|
||||||
|
|
||||||
# A dummy client class
|
|
||||||
class MyClassClient(Client): pass
|
|
||||||
|
|
||||||
Note that because MyClassLocal isn't used in the definition of
|
|
||||||
MyClassClient, it would actually be better to place it in a separate
|
|
||||||
module so the definition of MyClassLocal isn't executed when we only
|
|
||||||
instantiate a client.
|
|
||||||
|
|
||||||
The modules client and server should probably be renamed to Client and
|
|
||||||
Server in order to match the class names.
|
|
||||||
|
|
||||||
|
|
||||||
*** Security warning: this version requires that you have a file
|
|
||||||
$HOME/.python_keyfile at the server and client side containing two
|
|
||||||
comma- separated numbers. The security system at the moment makes no
|
|
||||||
guarantees of actuallng being secure -- however it requires that the
|
|
||||||
key file exists and contains the same numbers at both ends for this to
|
|
||||||
work. (You can specify an alternative keyfile in $PYTHON_KEYFILE).
|
|
||||||
Have a look at the Security class in security.py for details;
|
|
||||||
basically, if the key file contains (x, y), then the security server
|
|
||||||
class chooses a random number z (the challenge) in the range
|
|
||||||
10..100000 and the client must be able to produce pow(z, x, y)
|
|
||||||
(i.e. z**x mod y).
|
|
|
@ -1,156 +0,0 @@
|
||||||
"""RPC Client module."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import socket
|
|
||||||
import pickle
|
|
||||||
import builtins
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
# Default verbosity (0 = silent, 1 = print connections, 2 = print requests too)
|
|
||||||
VERBOSE = 1
|
|
||||||
|
|
||||||
|
|
||||||
class Client:
|
|
||||||
|
|
||||||
"""RPC Client class. No need to derive a class -- it's fully generic."""
|
|
||||||
|
|
||||||
def __init__(self, address, verbose = VERBOSE):
|
|
||||||
self._pre_init(address, verbose)
|
|
||||||
self._post_init()
|
|
||||||
|
|
||||||
def _pre_init(self, address, verbose = VERBOSE):
|
|
||||||
if type(address) == type(0):
|
|
||||||
address = ('', address)
|
|
||||||
self._address = address
|
|
||||||
self._verbose = verbose
|
|
||||||
if self._verbose: print("Connecting to %s ..." % repr(address))
|
|
||||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
self._socket.connect(address)
|
|
||||||
if self._verbose: print("Connected.")
|
|
||||||
self._lastid = 0 # Last id for which a reply has been received
|
|
||||||
self._nextid = 1 # Id of next request
|
|
||||||
self._replies = {} # Unprocessed replies
|
|
||||||
self._rf = self._socket.makefile('r')
|
|
||||||
self._wf = self._socket.makefile('w')
|
|
||||||
|
|
||||||
def _post_init(self):
|
|
||||||
self._methods = self._call('.methods')
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self._close()
|
|
||||||
|
|
||||||
def _close(self):
|
|
||||||
if self._rf: self._rf.close()
|
|
||||||
self._rf = None
|
|
||||||
if self._wf: self._wf.close()
|
|
||||||
self._wf = None
|
|
||||||
if self._socket: self._socket.close()
|
|
||||||
self._socket = None
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name in self._methods:
|
|
||||||
method = _stub(self, name)
|
|
||||||
setattr(self, name, method) # XXX circular reference
|
|
||||||
return method
|
|
||||||
raise AttributeError(name)
|
|
||||||
|
|
||||||
def _setverbose(self, verbose):
|
|
||||||
self._verbose = verbose
|
|
||||||
|
|
||||||
def _call(self, name, *args):
|
|
||||||
return self._vcall(name, args)
|
|
||||||
|
|
||||||
def _vcall(self, name, args):
|
|
||||||
return self._recv(self._vsend(name, args))
|
|
||||||
|
|
||||||
def _send(self, name, *args):
|
|
||||||
return self._vsend(name, args)
|
|
||||||
|
|
||||||
def _send_noreply(self, name, *args):
|
|
||||||
return self._vsend(name, args, 0)
|
|
||||||
|
|
||||||
def _vsend_noreply(self, name, args):
|
|
||||||
return self._vsend(name, args, 0)
|
|
||||||
|
|
||||||
def _vsend(self, name, args, wantreply = 1):
|
|
||||||
id = self._nextid
|
|
||||||
self._nextid = id+1
|
|
||||||
if not wantreply: id = -id
|
|
||||||
request = (name, args, id)
|
|
||||||
if self._verbose > 1: print("sending request: %s" % repr(request))
|
|
||||||
wp = pickle.Pickler(self._wf)
|
|
||||||
wp.dump(request)
|
|
||||||
return id
|
|
||||||
|
|
||||||
def _recv(self, id):
|
|
||||||
exception, value, rid = self._vrecv(id)
|
|
||||||
if rid != id:
|
|
||||||
raise RuntimeError("request/reply id mismatch: %d/%d" % (id, rid))
|
|
||||||
if exception is None:
|
|
||||||
return value
|
|
||||||
x = exception
|
|
||||||
if hasattr(builtins, exception):
|
|
||||||
x = getattr(builtins, exception)
|
|
||||||
elif exception in ('posix.error', 'mac.error'):
|
|
||||||
x = os.error
|
|
||||||
if x == exception:
|
|
||||||
exception = x
|
|
||||||
raise exception(value)
|
|
||||||
|
|
||||||
def _vrecv(self, id):
|
|
||||||
self._flush()
|
|
||||||
if id in self._replies:
|
|
||||||
if self._verbose > 1: print("retrieving previous reply, id = %d" % id)
|
|
||||||
reply = self._replies[id]
|
|
||||||
del self._replies[id]
|
|
||||||
return reply
|
|
||||||
aid = abs(id)
|
|
||||||
while 1:
|
|
||||||
if self._verbose > 1: print("waiting for reply, id = %d" % id)
|
|
||||||
rp = pickle.Unpickler(self._rf)
|
|
||||||
reply = rp.load()
|
|
||||||
del rp
|
|
||||||
if self._verbose > 1: print("got reply: %s" % repr(reply))
|
|
||||||
rid = reply[2]
|
|
||||||
arid = abs(rid)
|
|
||||||
if arid == aid:
|
|
||||||
if self._verbose > 1: print("got it")
|
|
||||||
return reply
|
|
||||||
self._replies[rid] = reply
|
|
||||||
if arid > aid:
|
|
||||||
if self._verbose > 1: print("got higher id, assume all ok")
|
|
||||||
return (None, None, id)
|
|
||||||
|
|
||||||
def _flush(self):
|
|
||||||
self._wf.flush()
|
|
||||||
|
|
||||||
|
|
||||||
from security import Security
|
|
||||||
|
|
||||||
|
|
||||||
class SecureClient(Client, Security):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
self._pre_init(*args)
|
|
||||||
Security.__init__(self)
|
|
||||||
self._wf.flush()
|
|
||||||
line = self._rf.readline()
|
|
||||||
challenge = int(line.strip())
|
|
||||||
response = self._encode_challenge(challenge)
|
|
||||||
line = repr(int(response))
|
|
||||||
if line[-1] in 'Ll': line = line[:-1]
|
|
||||||
self._wf.write(line + '\n')
|
|
||||||
self._wf.flush()
|
|
||||||
self._post_init()
|
|
||||||
|
|
||||||
class _stub:
|
|
||||||
|
|
||||||
"""Helper class for Client -- each instance serves as a method of the client."""
|
|
||||||
|
|
||||||
def __init__(self, client, name):
|
|
||||||
self._client = client
|
|
||||||
self._name = name
|
|
||||||
|
|
||||||
def __call__(self, *args):
|
|
||||||
return self._client._vcall(self._name, args)
|
|
|
@ -1,142 +0,0 @@
|
||||||
"Framework for command line interfaces like CVS. See class CmdFrameWork."
|
|
||||||
|
|
||||||
|
|
||||||
class CommandFrameWork:
|
|
||||||
|
|
||||||
"""Framework class for command line interfaces like CVS.
|
|
||||||
|
|
||||||
The general command line structure is
|
|
||||||
|
|
||||||
command [flags] subcommand [subflags] [argument] ...
|
|
||||||
|
|
||||||
There's a class variable GlobalFlags which specifies the
|
|
||||||
global flags options. Subcommands are defined by defining
|
|
||||||
methods named do_<subcommand>. Flags for the subcommand are
|
|
||||||
defined by defining class or instance variables named
|
|
||||||
flags_<subcommand>. If there's no command, method default()
|
|
||||||
is called. The __doc__ strings for the do_ methods are used
|
|
||||||
for the usage message, printed after the general usage message
|
|
||||||
which is the class variable UsageMessage. The class variable
|
|
||||||
PostUsageMessage is printed after all the do_ methods' __doc__
|
|
||||||
strings. The method's return value can be a suggested exit
|
|
||||||
status. [XXX Need to rewrite this to clarify it.]
|
|
||||||
|
|
||||||
Common usage is to derive a class, instantiate it, and then call its
|
|
||||||
run() method; by default this takes its arguments from sys.argv[1:].
|
|
||||||
"""
|
|
||||||
|
|
||||||
UsageMessage = \
|
|
||||||
"usage: (name)s [flags] subcommand [subflags] [argument] ..."
|
|
||||||
|
|
||||||
PostUsageMessage = None
|
|
||||||
|
|
||||||
GlobalFlags = ''
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Constructor, present for completeness."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def run(self, args = None):
|
|
||||||
"""Process flags, subcommand and options, then run it."""
|
|
||||||
import getopt, sys
|
|
||||||
if args is None: args = sys.argv[1:]
|
|
||||||
try:
|
|
||||||
opts, args = getopt.getopt(args, self.GlobalFlags)
|
|
||||||
except getopt.error as msg:
|
|
||||||
return self.usage(msg)
|
|
||||||
self.options(opts)
|
|
||||||
if not args:
|
|
||||||
self.ready()
|
|
||||||
return self.default()
|
|
||||||
else:
|
|
||||||
cmd = args[0]
|
|
||||||
mname = 'do_' + cmd
|
|
||||||
fname = 'flags_' + cmd
|
|
||||||
try:
|
|
||||||
method = getattr(self, mname)
|
|
||||||
except AttributeError:
|
|
||||||
return self.usage("command %r unknown" % (cmd,))
|
|
||||||
try:
|
|
||||||
flags = getattr(self, fname)
|
|
||||||
except AttributeError:
|
|
||||||
flags = ''
|
|
||||||
try:
|
|
||||||
opts, args = getopt.getopt(args[1:], flags)
|
|
||||||
except getopt.error as msg:
|
|
||||||
return self.usage(
|
|
||||||
"subcommand %s: " % cmd + str(msg))
|
|
||||||
self.ready()
|
|
||||||
return method(opts, args)
|
|
||||||
|
|
||||||
def options(self, opts):
|
|
||||||
"""Process the options retrieved by getopt.
|
|
||||||
Override this if you have any options."""
|
|
||||||
if opts:
|
|
||||||
print("-"*40)
|
|
||||||
print("Options:")
|
|
||||||
for o, a in opts:
|
|
||||||
print('option', o, 'value', repr(a))
|
|
||||||
print("-"*40)
|
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
"""Called just before calling the subcommand."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def usage(self, msg = None):
|
|
||||||
"""Print usage message. Return suitable exit code (2)."""
|
|
||||||
if msg: print(msg)
|
|
||||||
print(self.UsageMessage % {'name': self.__class__.__name__})
|
|
||||||
docstrings = {}
|
|
||||||
c = self.__class__
|
|
||||||
while 1:
|
|
||||||
for name in dir(c):
|
|
||||||
if name[:3] == 'do_':
|
|
||||||
if name in docstrings:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
doc = getattr(c, name).__doc__
|
|
||||||
except:
|
|
||||||
doc = None
|
|
||||||
if doc:
|
|
||||||
docstrings[name] = doc
|
|
||||||
if not c.__bases__:
|
|
||||||
break
|
|
||||||
c = c.__bases__[0]
|
|
||||||
if docstrings:
|
|
||||||
print("where subcommand can be:")
|
|
||||||
for name in sorted(docstrings.keys()):
|
|
||||||
print(docstrings[name])
|
|
||||||
if self.PostUsageMessage:
|
|
||||||
print(self.PostUsageMessage)
|
|
||||||
return 2
|
|
||||||
|
|
||||||
def default(self):
|
|
||||||
"""Default method, called when no subcommand is given.
|
|
||||||
You should always override this."""
|
|
||||||
print("Nobody expects the Spanish Inquisition!")
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
"""Test script -- called when this module is run as a script."""
|
|
||||||
import sys
|
|
||||||
class Hello(CommandFrameWork):
|
|
||||||
def do_hello(self, opts, args):
|
|
||||||
"hello -- print 'hello world', needs no arguments"
|
|
||||||
print("Hello, world")
|
|
||||||
x = Hello()
|
|
||||||
tests = [
|
|
||||||
[],
|
|
||||||
['hello'],
|
|
||||||
['spam'],
|
|
||||||
['-x'],
|
|
||||||
['hello', '-x'],
|
|
||||||
None,
|
|
||||||
]
|
|
||||||
for t in tests:
|
|
||||||
print('-'*10, t, '-'*10)
|
|
||||||
sts = x.run(t)
|
|
||||||
print("Exit status:", repr(sts))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
test()
|
|
|
@ -1,213 +0,0 @@
|
||||||
"""Compare local and remote dictionaries and transfer differing files -- like rdist."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from reprlib import repr
|
|
||||||
import FSProxy
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
|
|
||||||
def raw_input(prompt):
|
|
||||||
sys.stdout.write(prompt)
|
|
||||||
sys.stdout.flush()
|
|
||||||
return sys.stdin.readline()
|
|
||||||
|
|
||||||
def main():
|
|
||||||
pwd = os.getcwd()
|
|
||||||
s = input("chdir [%s] " % pwd)
|
|
||||||
if s:
|
|
||||||
os.chdir(s)
|
|
||||||
pwd = os.getcwd()
|
|
||||||
host = ask("host", 'voorn.cwi.nl')
|
|
||||||
port = 4127
|
|
||||||
verbose = 1
|
|
||||||
mode = ''
|
|
||||||
print("""\
|
|
||||||
Mode should be a string of characters, indicating what to do with differences.
|
|
||||||
r - read different files to local file system
|
|
||||||
w - write different files to remote file system
|
|
||||||
c - create new files, either remote or local
|
|
||||||
d - delete disappearing files, either remote or local
|
|
||||||
""")
|
|
||||||
s = input("mode [%s] " % mode)
|
|
||||||
if s: mode = s
|
|
||||||
address = (host, port)
|
|
||||||
t1 = time.time()
|
|
||||||
local = FSProxy.FSProxyLocal()
|
|
||||||
remote = FSProxy.FSProxyClient(address, verbose)
|
|
||||||
compare(local, remote, mode)
|
|
||||||
remote._close()
|
|
||||||
local._close()
|
|
||||||
t2 = time.time()
|
|
||||||
dt = t2-t1
|
|
||||||
mins, secs = divmod(dt, 60)
|
|
||||||
print(mins, "minutes and", round(secs), "seconds")
|
|
||||||
input("[Return to exit] ")
|
|
||||||
|
|
||||||
def ask(prompt, default):
|
|
||||||
s = input("%s [%s] " % (prompt, default))
|
|
||||||
return s or default
|
|
||||||
|
|
||||||
def askint(prompt, default):
|
|
||||||
s = input("%s [%s] " % (prompt, str(default)))
|
|
||||||
if s: return string.atoi(s)
|
|
||||||
return default
|
|
||||||
|
|
||||||
def compare(local, remote, mode):
|
|
||||||
print()
|
|
||||||
print("PWD =", repr(os.getcwd()))
|
|
||||||
sums_id = remote._send('sumlist')
|
|
||||||
subdirs_id = remote._send('listsubdirs')
|
|
||||||
remote._flush()
|
|
||||||
print("calculating local sums ...")
|
|
||||||
lsumdict = {}
|
|
||||||
for name, info in local.sumlist():
|
|
||||||
lsumdict[name] = info
|
|
||||||
print("getting remote sums ...")
|
|
||||||
sums = remote._recv(sums_id)
|
|
||||||
print("got", len(sums))
|
|
||||||
rsumdict = {}
|
|
||||||
for name, rsum in sums:
|
|
||||||
rsumdict[name] = rsum
|
|
||||||
if name not in lsumdict:
|
|
||||||
print(repr(name), "only remote")
|
|
||||||
if 'r' in mode and 'c' in mode:
|
|
||||||
recvfile(local, remote, name)
|
|
||||||
else:
|
|
||||||
lsum = lsumdict[name]
|
|
||||||
if lsum != rsum:
|
|
||||||
print(repr(name), end=' ')
|
|
||||||
rmtime = remote.mtime(name)
|
|
||||||
lmtime = local.mtime(name)
|
|
||||||
if rmtime > lmtime:
|
|
||||||
print("remote newer", end=' ')
|
|
||||||
if 'r' in mode:
|
|
||||||
recvfile(local, remote, name)
|
|
||||||
elif lmtime > rmtime:
|
|
||||||
print("local newer", end=' ')
|
|
||||||
if 'w' in mode:
|
|
||||||
sendfile(local, remote, name)
|
|
||||||
else:
|
|
||||||
print("same mtime but different sum?!?!", end=' ')
|
|
||||||
print()
|
|
||||||
for name in lsumdict.keys():
|
|
||||||
if not list(rsumdict.keys()):
|
|
||||||
print(repr(name), "only locally", end=' ')
|
|
||||||
fl()
|
|
||||||
if 'w' in mode and 'c' in mode:
|
|
||||||
sendfile(local, remote, name)
|
|
||||||
elif 'r' in mode and 'd' in mode:
|
|
||||||
os.unlink(name)
|
|
||||||
print("removed.")
|
|
||||||
print()
|
|
||||||
print("gettin subdirs ...")
|
|
||||||
subdirs = remote._recv(subdirs_id)
|
|
||||||
common = []
|
|
||||||
for name in subdirs:
|
|
||||||
if local.isdir(name):
|
|
||||||
print("Common subdirectory", repr(name))
|
|
||||||
common.append(name)
|
|
||||||
else:
|
|
||||||
print("Remote subdirectory", repr(name), "not found locally")
|
|
||||||
if 'r' in mode and 'c' in mode:
|
|
||||||
pr = "Create local subdirectory %s? [y] " % \
|
|
||||||
repr(name)
|
|
||||||
if 'y' in mode:
|
|
||||||
ok = 'y'
|
|
||||||
else:
|
|
||||||
ok = ask(pr, "y")
|
|
||||||
if ok[:1] in ('y', 'Y'):
|
|
||||||
local.mkdir(name)
|
|
||||||
print("Subdirectory %s made" % \
|
|
||||||
repr(name))
|
|
||||||
common.append(name)
|
|
||||||
lsubdirs = local.listsubdirs()
|
|
||||||
for name in lsubdirs:
|
|
||||||
if name not in subdirs:
|
|
||||||
print("Local subdirectory", repr(name), "not found remotely")
|
|
||||||
for name in common:
|
|
||||||
print("Entering subdirectory", repr(name))
|
|
||||||
local.cd(name)
|
|
||||||
remote.cd(name)
|
|
||||||
compare(local, remote, mode)
|
|
||||||
remote.back()
|
|
||||||
local.back()
|
|
||||||
|
|
||||||
def sendfile(local, remote, name):
|
|
||||||
try:
|
|
||||||
remote.create(name)
|
|
||||||
except (IOError, os.error) as msg:
|
|
||||||
print("cannot create:", msg)
|
|
||||||
return
|
|
||||||
|
|
||||||
print("sending ...", end=' ')
|
|
||||||
fl()
|
|
||||||
|
|
||||||
data = open(name).read()
|
|
||||||
|
|
||||||
t1 = time.time()
|
|
||||||
|
|
||||||
remote._send_noreply('write', name, data)
|
|
||||||
remote._flush()
|
|
||||||
|
|
||||||
t2 = time.time()
|
|
||||||
|
|
||||||
dt = t2-t1
|
|
||||||
print(len(data), "bytes in", round(dt), "seconds", end=' ')
|
|
||||||
if dt:
|
|
||||||
print("i.e.", round(len(data)/dt), "bytes/sec", end=' ')
|
|
||||||
print()
|
|
||||||
|
|
||||||
def recvfile(local, remote, name):
|
|
||||||
ok = 0
|
|
||||||
try:
|
|
||||||
rv = recvfile_real(local, remote, name)
|
|
||||||
ok = 1
|
|
||||||
return rv
|
|
||||||
finally:
|
|
||||||
if not ok:
|
|
||||||
print("*** recvfile of %r failed, deleting" % (name,))
|
|
||||||
local.delete(name)
|
|
||||||
|
|
||||||
def recvfile_real(local, remote, name):
|
|
||||||
try:
|
|
||||||
local.create(name)
|
|
||||||
except (IOError, os.error) as msg:
|
|
||||||
print("cannot create:", msg)
|
|
||||||
return
|
|
||||||
|
|
||||||
print("receiving ...", end=' ')
|
|
||||||
fl()
|
|
||||||
|
|
||||||
f = open(name, 'w')
|
|
||||||
t1 = time.time()
|
|
||||||
|
|
||||||
length = 4*1024
|
|
||||||
offset = 0
|
|
||||||
id = remote._send('read', name, offset, length)
|
|
||||||
remote._flush()
|
|
||||||
while 1:
|
|
||||||
newoffset = offset + length
|
|
||||||
newid = remote._send('read', name, newoffset, length)
|
|
||||||
data = remote._recv(id)
|
|
||||||
id = newid
|
|
||||||
if not data: break
|
|
||||||
f.seek(offset)
|
|
||||||
f.write(data)
|
|
||||||
offset = newoffset
|
|
||||||
size = f.tell()
|
|
||||||
|
|
||||||
t2 = time.time()
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
dt = t2-t1
|
|
||||||
print(size, "bytes in", round(dt), "seconds", end=' ')
|
|
||||||
if dt:
|
|
||||||
print("i.e.", int(size//dt), "bytes/sec", end=' ')
|
|
||||||
print()
|
|
||||||
remote._recv(id) # ignored
|
|
||||||
|
|
||||||
def fl():
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -1,359 +0,0 @@
|
||||||
"""Utilities for CVS administration."""
|
|
||||||
|
|
||||||
import string
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import md5
|
|
||||||
import fnmatch
|
|
||||||
|
|
||||||
if not hasattr(time, 'timezone'):
|
|
||||||
time.timezone = 0
|
|
||||||
|
|
||||||
class File:
|
|
||||||
|
|
||||||
"""Represent a file's status.
|
|
||||||
|
|
||||||
Instance variables:
|
|
||||||
|
|
||||||
file -- the filename (no slashes), None if uninitialized
|
|
||||||
lseen -- true if the data for the local file is up to date
|
|
||||||
eseen -- true if the data from the CVS/Entries entry is up to date
|
|
||||||
(this implies that the entry must be written back)
|
|
||||||
rseen -- true if the data for the remote file is up to date
|
|
||||||
proxy -- RCSProxy instance used to contact the server, or None
|
|
||||||
|
|
||||||
Note that lseen and rseen don't necessary mean that a local
|
|
||||||
or remote file *exists* -- they indicate that we've checked it.
|
|
||||||
However, eseen means that this instance corresponds to an
|
|
||||||
entry in the CVS/Entries file.
|
|
||||||
|
|
||||||
If lseen is true:
|
|
||||||
|
|
||||||
lsum -- checksum of the local file, None if no local file
|
|
||||||
lctime -- ctime of the local file, None if no local file
|
|
||||||
lmtime -- mtime of the local file, None if no local file
|
|
||||||
|
|
||||||
If eseen is true:
|
|
||||||
|
|
||||||
erev -- revision, None if this is a no revision (not '0')
|
|
||||||
enew -- true if this is an uncommitted added file
|
|
||||||
edeleted -- true if this is an uncommitted removed file
|
|
||||||
ectime -- ctime of last local file corresponding to erev
|
|
||||||
emtime -- mtime of last local file corresponding to erev
|
|
||||||
extra -- 5th string from CVS/Entries file
|
|
||||||
|
|
||||||
If rseen is true:
|
|
||||||
|
|
||||||
rrev -- revision of head, None if non-existent
|
|
||||||
rsum -- checksum of that revision, Non if non-existent
|
|
||||||
|
|
||||||
If eseen and rseen are both true:
|
|
||||||
|
|
||||||
esum -- checksum of revision erev, None if no revision
|
|
||||||
|
|
||||||
Note
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, file = None):
|
|
||||||
if file and '/' in file:
|
|
||||||
raise ValueError("no slash allowed in file")
|
|
||||||
self.file = file
|
|
||||||
self.lseen = self.eseen = self.rseen = 0
|
|
||||||
self.proxy = None
|
|
||||||
|
|
||||||
def __cmp__(self, other):
|
|
||||||
return cmp(self.file, other.file)
|
|
||||||
|
|
||||||
def getlocal(self):
|
|
||||||
try:
|
|
||||||
self.lmtime, self.lctime = os.stat(self.file)[-2:]
|
|
||||||
except os.error:
|
|
||||||
self.lmtime = self.lctime = self.lsum = None
|
|
||||||
else:
|
|
||||||
self.lsum = md5.new(open(self.file).read()).digest()
|
|
||||||
self.lseen = 1
|
|
||||||
|
|
||||||
def getentry(self, line):
|
|
||||||
words = string.splitfields(line, '/')
|
|
||||||
if self.file and words[1] != self.file:
|
|
||||||
raise ValueError("file name mismatch")
|
|
||||||
self.file = words[1]
|
|
||||||
self.erev = words[2]
|
|
||||||
self.edeleted = 0
|
|
||||||
self.enew = 0
|
|
||||||
self.ectime = self.emtime = None
|
|
||||||
if self.erev[:1] == '-':
|
|
||||||
self.edeleted = 1
|
|
||||||
self.erev = self.erev[1:]
|
|
||||||
if self.erev == '0':
|
|
||||||
self.erev = None
|
|
||||||
self.enew = 1
|
|
||||||
else:
|
|
||||||
dates = words[3]
|
|
||||||
self.ectime = unctime(dates[:24])
|
|
||||||
self.emtime = unctime(dates[25:])
|
|
||||||
self.extra = words[4]
|
|
||||||
if self.rseen:
|
|
||||||
self.getesum()
|
|
||||||
self.eseen = 1
|
|
||||||
|
|
||||||
def getremote(self, proxy = None):
|
|
||||||
if proxy:
|
|
||||||
self.proxy = proxy
|
|
||||||
try:
|
|
||||||
self.rrev = self.proxy.head(self.file)
|
|
||||||
except (os.error, IOError):
|
|
||||||
self.rrev = None
|
|
||||||
if self.rrev:
|
|
||||||
self.rsum = self.proxy.sum(self.file)
|
|
||||||
else:
|
|
||||||
self.rsum = None
|
|
||||||
if self.eseen:
|
|
||||||
self.getesum()
|
|
||||||
self.rseen = 1
|
|
||||||
|
|
||||||
def getesum(self):
|
|
||||||
if self.erev == self.rrev:
|
|
||||||
self.esum = self.rsum
|
|
||||||
elif self.erev:
|
|
||||||
name = (self.file, self.erev)
|
|
||||||
self.esum = self.proxy.sum(name)
|
|
||||||
else:
|
|
||||||
self.esum = None
|
|
||||||
|
|
||||||
def putentry(self):
|
|
||||||
"""Return a line suitable for inclusion in CVS/Entries.
|
|
||||||
|
|
||||||
The returned line is terminated by a newline.
|
|
||||||
If no entry should be written for this file,
|
|
||||||
return "".
|
|
||||||
"""
|
|
||||||
if not self.eseen:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
rev = self.erev or '0'
|
|
||||||
if self.edeleted:
|
|
||||||
rev = '-' + rev
|
|
||||||
if self.enew:
|
|
||||||
dates = 'Initial ' + self.file
|
|
||||||
else:
|
|
||||||
dates = gmctime(self.ectime) + ' ' + \
|
|
||||||
gmctime(self.emtime)
|
|
||||||
return "/%s/%s/%s/%s/\n" % (
|
|
||||||
self.file,
|
|
||||||
rev,
|
|
||||||
dates,
|
|
||||||
self.extra)
|
|
||||||
|
|
||||||
def report(self):
|
|
||||||
print('-'*50)
|
|
||||||
def r(key, repr=repr, self=self):
|
|
||||||
try:
|
|
||||||
value = repr(getattr(self, key))
|
|
||||||
except AttributeError:
|
|
||||||
value = "?"
|
|
||||||
print("%-15s:" % key, value)
|
|
||||||
r("file")
|
|
||||||
if self.lseen:
|
|
||||||
r("lsum", hexify)
|
|
||||||
r("lctime", gmctime)
|
|
||||||
r("lmtime", gmctime)
|
|
||||||
if self.eseen:
|
|
||||||
r("erev")
|
|
||||||
r("enew")
|
|
||||||
r("edeleted")
|
|
||||||
r("ectime", gmctime)
|
|
||||||
r("emtime", gmctime)
|
|
||||||
if self.rseen:
|
|
||||||
r("rrev")
|
|
||||||
r("rsum", hexify)
|
|
||||||
if self.eseen:
|
|
||||||
r("esum", hexify)
|
|
||||||
|
|
||||||
|
|
||||||
class CVS:
|
|
||||||
|
|
||||||
"""Represent the contents of a CVS admin file (and more).
|
|
||||||
|
|
||||||
Class variables:
|
|
||||||
|
|
||||||
FileClass -- the class to be instantiated for entries
|
|
||||||
(this should be derived from class File above)
|
|
||||||
IgnoreList -- shell patterns for local files to be ignored
|
|
||||||
|
|
||||||
Instance variables:
|
|
||||||
|
|
||||||
entries -- a dictionary containing File instances keyed by
|
|
||||||
their file name
|
|
||||||
proxy -- an RCSProxy instance, or None
|
|
||||||
"""
|
|
||||||
|
|
||||||
FileClass = File
|
|
||||||
|
|
||||||
IgnoreList = ['.*', '@*', ',*', '*~', '*.o', '*.a', '*.so', '*.pyc']
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.entries = {}
|
|
||||||
self.proxy = None
|
|
||||||
|
|
||||||
def setproxy(self, proxy):
|
|
||||||
if proxy is self.proxy:
|
|
||||||
return
|
|
||||||
self.proxy = proxy
|
|
||||||
for e in list(self.entries.values()):
|
|
||||||
e.rseen = 0
|
|
||||||
|
|
||||||
def getentries(self):
|
|
||||||
"""Read the contents of CVS/Entries"""
|
|
||||||
self.entries = {}
|
|
||||||
f = self.cvsopen("Entries")
|
|
||||||
while 1:
|
|
||||||
line = f.readline()
|
|
||||||
if not line: break
|
|
||||||
e = self.FileClass()
|
|
||||||
e.getentry(line)
|
|
||||||
self.entries[e.file] = e
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def putentries(self):
|
|
||||||
"""Write CVS/Entries back"""
|
|
||||||
f = self.cvsopen("Entries", 'w')
|
|
||||||
for e in list(self.values()):
|
|
||||||
f.write(e.putentry())
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def getlocalfiles(self):
|
|
||||||
entries_keys = set(self.entries.keys())
|
|
||||||
addlist = os.listdir(os.curdir)
|
|
||||||
for name in addlist:
|
|
||||||
if not self.ignored(name):
|
|
||||||
entries_keys.add(name)
|
|
||||||
for file in sorted(entries_keys):
|
|
||||||
try:
|
|
||||||
e = self.entries[file]
|
|
||||||
except KeyError:
|
|
||||||
e = self.entries[file] = self.FileClass(file)
|
|
||||||
e.getlocal()
|
|
||||||
|
|
||||||
def getremotefiles(self, proxy = None):
|
|
||||||
if proxy:
|
|
||||||
self.proxy = proxy
|
|
||||||
if not self.proxy:
|
|
||||||
raise RuntimeError("no RCS proxy")
|
|
||||||
addlist = self.proxy.listfiles()
|
|
||||||
for file in addlist:
|
|
||||||
try:
|
|
||||||
e = self.entries[file]
|
|
||||||
except KeyError:
|
|
||||||
e = self.entries[file] = self.FileClass(file)
|
|
||||||
e.getremote(self.proxy)
|
|
||||||
|
|
||||||
def report(self):
|
|
||||||
for e in list(self.values()):
|
|
||||||
e.report()
|
|
||||||
print('-'*50)
|
|
||||||
|
|
||||||
def keys(self):
|
|
||||||
return sorted(self.entries.keys())
|
|
||||||
|
|
||||||
def values(self):
|
|
||||||
def value(key, self=self):
|
|
||||||
return self.entries[key]
|
|
||||||
return [value(k) for k in self.keys()]
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
def item(key, self=self):
|
|
||||||
return (key, self.entries[key])
|
|
||||||
return [item(k) for k in self.keys()]
|
|
||||||
|
|
||||||
def cvsexists(self, file):
|
|
||||||
file = os.path.join("CVS", file)
|
|
||||||
return os.path.exists(file)
|
|
||||||
|
|
||||||
def cvsopen(self, file, mode = 'r'):
|
|
||||||
file = os.path.join("CVS", file)
|
|
||||||
if 'r' not in mode:
|
|
||||||
self.backup(file)
|
|
||||||
return open(file, mode)
|
|
||||||
|
|
||||||
def backup(self, file):
|
|
||||||
if os.path.isfile(file):
|
|
||||||
bfile = file + '~'
|
|
||||||
try: os.unlink(bfile)
|
|
||||||
except os.error: pass
|
|
||||||
os.rename(file, bfile)
|
|
||||||
|
|
||||||
def ignored(self, file):
|
|
||||||
if os.path.isdir(file): return True
|
|
||||||
for pat in self.IgnoreList:
|
|
||||||
if fnmatch.fnmatch(file, pat): return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# hexify and unhexify are useful to print MD5 checksums in hex format
|
|
||||||
|
|
||||||
hexify_format = '%02x' * 16
|
|
||||||
def hexify(sum):
|
|
||||||
"Return a hex representation of a 16-byte string (e.g. an MD5 digest)"
|
|
||||||
if sum is None:
|
|
||||||
return "None"
|
|
||||||
return hexify_format % tuple(map(ord, sum))
|
|
||||||
|
|
||||||
def unhexify(hexsum):
|
|
||||||
"Return the original from a hexified string"
|
|
||||||
if hexsum == "None":
|
|
||||||
return None
|
|
||||||
sum = ''
|
|
||||||
for i in range(0, len(hexsum), 2):
|
|
||||||
sum = sum + chr(string.atoi(hexsum[i:i+2], 16))
|
|
||||||
return sum
|
|
||||||
|
|
||||||
|
|
||||||
unctime_monthmap = {}
|
|
||||||
def unctime(date):
|
|
||||||
if date == "None": return None
|
|
||||||
if not unctime_monthmap:
|
|
||||||
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
|
||||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
||||||
i = 0
|
|
||||||
for m in months:
|
|
||||||
i = i+1
|
|
||||||
unctime_monthmap[m] = i
|
|
||||||
words = string.split(date) # Day Mon DD HH:MM:SS YEAR
|
|
||||||
year = string.atoi(words[4])
|
|
||||||
month = unctime_monthmap[words[1]]
|
|
||||||
day = string.atoi(words[2])
|
|
||||||
[hh, mm, ss] = list(map(string.atoi, string.splitfields(words[3], ':')))
|
|
||||||
ss = ss - time.timezone
|
|
||||||
return time.mktime((year, month, day, hh, mm, ss, 0, 0, 0))
|
|
||||||
|
|
||||||
def gmctime(t):
|
|
||||||
if t is None: return "None"
|
|
||||||
return time.asctime(time.gmtime(t))
|
|
||||||
|
|
||||||
def test_unctime():
|
|
||||||
now = int(time.time())
|
|
||||||
t = time.gmtime(now)
|
|
||||||
at = time.asctime(t)
|
|
||||||
print('GMT', now, at)
|
|
||||||
print('timezone', time.timezone)
|
|
||||||
print('local', time.ctime(now))
|
|
||||||
u = unctime(at)
|
|
||||||
print('unctime()', u)
|
|
||||||
gu = time.gmtime(u)
|
|
||||||
print('->', gu)
|
|
||||||
print(time.asctime(gu))
|
|
||||||
|
|
||||||
def test():
|
|
||||||
x = CVS()
|
|
||||||
x.getentries()
|
|
||||||
x.getlocalfiles()
|
|
||||||
## x.report()
|
|
||||||
import rcsclient
|
|
||||||
proxy = rcsclient.openrcsclient()
|
|
||||||
x.getremotefiles(proxy)
|
|
||||||
x.report()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test()
|
|
|
@ -1,279 +0,0 @@
|
||||||
"""CVS locking algorithm.
|
|
||||||
|
|
||||||
CVS locking strategy
|
|
||||||
====================
|
|
||||||
|
|
||||||
As reverse engineered from the CVS 1.3 sources (file lock.c):
|
|
||||||
|
|
||||||
- Locking is done on a per repository basis (but a process can hold
|
|
||||||
write locks for multiple directories); all lock files are placed in
|
|
||||||
the repository and have names beginning with "#cvs.".
|
|
||||||
|
|
||||||
- Before even attempting to lock, a file "#cvs.tfl.<pid>" is created
|
|
||||||
(and removed again), to test that we can write the repository. [The
|
|
||||||
algorithm can still be fooled (1) if the repository's mode is changed
|
|
||||||
while attempting to lock; (2) if this file exists and is writable but
|
|
||||||
the directory is not.]
|
|
||||||
|
|
||||||
- While creating the actual read/write lock files (which may exist for
|
|
||||||
a long time), a "meta-lock" is held. The meta-lock is a directory
|
|
||||||
named "#cvs.lock" in the repository. The meta-lock is also held while
|
|
||||||
a write lock is held.
|
|
||||||
|
|
||||||
- To set a read lock:
|
|
||||||
|
|
||||||
- acquire the meta-lock
|
|
||||||
- create the file "#cvs.rfl.<pid>"
|
|
||||||
- release the meta-lock
|
|
||||||
|
|
||||||
- To set a write lock:
|
|
||||||
|
|
||||||
- acquire the meta-lock
|
|
||||||
- check that there are no files called "#cvs.rfl.*"
|
|
||||||
- if there are, release the meta-lock, sleep, try again
|
|
||||||
- create the file "#cvs.wfl.<pid>"
|
|
||||||
|
|
||||||
- To release a write lock:
|
|
||||||
|
|
||||||
- remove the file "#cvs.wfl.<pid>"
|
|
||||||
- rmdir the meta-lock
|
|
||||||
|
|
||||||
- To release a read lock:
|
|
||||||
|
|
||||||
- remove the file "#cvs.rfl.<pid>"
|
|
||||||
|
|
||||||
|
|
||||||
Additional notes
|
|
||||||
----------------
|
|
||||||
|
|
||||||
- A process should read-lock at most one repository at a time.
|
|
||||||
|
|
||||||
- A process may write-lock as many repositories as it wishes (to avoid
|
|
||||||
deadlocks, I presume it should always lock them top-down in the
|
|
||||||
directory hierarchy).
|
|
||||||
|
|
||||||
- A process should make sure it removes all its lock files and
|
|
||||||
directories when it crashes.
|
|
||||||
|
|
||||||
- Limitation: one user id should not be committing files into the same
|
|
||||||
repository at the same time.
|
|
||||||
|
|
||||||
|
|
||||||
Turn this into Python code
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
rl = ReadLock(repository, waittime)
|
|
||||||
|
|
||||||
wl = WriteLock(repository, waittime)
|
|
||||||
|
|
||||||
list = MultipleWriteLock([repository1, repository2, ...], waittime)
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import stat
|
|
||||||
import pwd
|
|
||||||
|
|
||||||
|
|
||||||
# Default wait time
|
|
||||||
DELAY = 10
|
|
||||||
|
|
||||||
|
|
||||||
# XXX This should be the same on all Unix versions
|
|
||||||
EEXIST = 17
|
|
||||||
|
|
||||||
|
|
||||||
# Files used for locking (must match cvs.h in the CVS sources)
|
|
||||||
CVSLCK = "#cvs.lck"
|
|
||||||
CVSRFL = "#cvs.rfl."
|
|
||||||
CVSWFL = "#cvs.wfl."
|
|
||||||
|
|
||||||
|
|
||||||
class Error:
|
|
||||||
|
|
||||||
def __init__(self, msg):
|
|
||||||
self.msg = msg
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return repr(self.msg)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str(self.msg)
|
|
||||||
|
|
||||||
|
|
||||||
class Locked(Error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Lock:
|
|
||||||
|
|
||||||
def __init__(self, repository = ".", delay = DELAY):
|
|
||||||
self.repository = repository
|
|
||||||
self.delay = delay
|
|
||||||
self.lockdir = None
|
|
||||||
self.lockfile = None
|
|
||||||
pid = repr(os.getpid())
|
|
||||||
self.cvslck = self.join(CVSLCK)
|
|
||||||
self.cvsrfl = self.join(CVSRFL + pid)
|
|
||||||
self.cvswfl = self.join(CVSWFL + pid)
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
print("__del__")
|
|
||||||
self.unlock()
|
|
||||||
|
|
||||||
def setlockdir(self):
|
|
||||||
while 1:
|
|
||||||
try:
|
|
||||||
self.lockdir = self.cvslck
|
|
||||||
os.mkdir(self.cvslck, 0o777)
|
|
||||||
return
|
|
||||||
except os.error as msg:
|
|
||||||
self.lockdir = None
|
|
||||||
if msg.args[0] == EEXIST:
|
|
||||||
try:
|
|
||||||
st = os.stat(self.cvslck)
|
|
||||||
except os.error:
|
|
||||||
continue
|
|
||||||
self.sleep(st)
|
|
||||||
continue
|
|
||||||
raise Error("failed to lock %s: %s" % (
|
|
||||||
self.repository, msg))
|
|
||||||
|
|
||||||
def unlock(self):
|
|
||||||
self.unlockfile()
|
|
||||||
self.unlockdir()
|
|
||||||
|
|
||||||
def unlockfile(self):
|
|
||||||
if self.lockfile:
|
|
||||||
print("unlink", self.lockfile)
|
|
||||||
try:
|
|
||||||
os.unlink(self.lockfile)
|
|
||||||
except os.error:
|
|
||||||
pass
|
|
||||||
self.lockfile = None
|
|
||||||
|
|
||||||
def unlockdir(self):
|
|
||||||
if self.lockdir:
|
|
||||||
print("rmdir", self.lockdir)
|
|
||||||
try:
|
|
||||||
os.rmdir(self.lockdir)
|
|
||||||
except os.error:
|
|
||||||
pass
|
|
||||||
self.lockdir = None
|
|
||||||
|
|
||||||
def sleep(self, st):
|
|
||||||
sleep(st, self.repository, self.delay)
|
|
||||||
|
|
||||||
def join(self, name):
|
|
||||||
return os.path.join(self.repository, name)
|
|
||||||
|
|
||||||
|
|
||||||
def sleep(st, repository, delay):
|
|
||||||
if delay <= 0:
|
|
||||||
raise Locked(st)
|
|
||||||
uid = st[stat.ST_UID]
|
|
||||||
try:
|
|
||||||
pwent = pwd.getpwuid(uid)
|
|
||||||
user = pwent[0]
|
|
||||||
except KeyError:
|
|
||||||
user = "uid %d" % uid
|
|
||||||
print("[%s]" % time.ctime(time.time())[11:19], end=' ')
|
|
||||||
print("Waiting for %s's lock in" % user, repository)
|
|
||||||
time.sleep(delay)
|
|
||||||
|
|
||||||
|
|
||||||
class ReadLock(Lock):
|
|
||||||
|
|
||||||
def __init__(self, repository, delay = DELAY):
|
|
||||||
Lock.__init__(self, repository, delay)
|
|
||||||
ok = 0
|
|
||||||
try:
|
|
||||||
self.setlockdir()
|
|
||||||
self.lockfile = self.cvsrfl
|
|
||||||
fp = open(self.lockfile, 'w')
|
|
||||||
fp.close()
|
|
||||||
ok = 1
|
|
||||||
finally:
|
|
||||||
if not ok:
|
|
||||||
self.unlockfile()
|
|
||||||
self.unlockdir()
|
|
||||||
|
|
||||||
|
|
||||||
class WriteLock(Lock):
|
|
||||||
|
|
||||||
def __init__(self, repository, delay = DELAY):
|
|
||||||
Lock.__init__(self, repository, delay)
|
|
||||||
self.setlockdir()
|
|
||||||
while 1:
|
|
||||||
uid = self.readers_exist()
|
|
||||||
if not uid:
|
|
||||||
break
|
|
||||||
self.unlockdir()
|
|
||||||
self.sleep(uid)
|
|
||||||
self.lockfile = self.cvswfl
|
|
||||||
fp = open(self.lockfile, 'w')
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
def readers_exist(self):
|
|
||||||
n = len(CVSRFL)
|
|
||||||
for name in os.listdir(self.repository):
|
|
||||||
if name[:n] == CVSRFL:
|
|
||||||
try:
|
|
||||||
st = os.stat(self.join(name))
|
|
||||||
except os.error:
|
|
||||||
continue
|
|
||||||
return st
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def MultipleWriteLock(repositories, delay = DELAY):
|
|
||||||
while 1:
|
|
||||||
locks = []
|
|
||||||
for r in repositories:
|
|
||||||
try:
|
|
||||||
locks.append(WriteLock(r, 0))
|
|
||||||
except Locked as instance:
|
|
||||||
del locks
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
sleep(instance.msg, r, delay)
|
|
||||||
return list
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
import sys
|
|
||||||
if sys.argv[1:]:
|
|
||||||
repository = sys.argv[1]
|
|
||||||
else:
|
|
||||||
repository = "."
|
|
||||||
rl = None
|
|
||||||
wl = None
|
|
||||||
try:
|
|
||||||
print("attempting write lock ...")
|
|
||||||
wl = WriteLock(repository)
|
|
||||||
print("got it.")
|
|
||||||
wl.unlock()
|
|
||||||
print("attempting read lock ...")
|
|
||||||
rl = ReadLock(repository)
|
|
||||||
print("got it.")
|
|
||||||
rl.unlock()
|
|
||||||
finally:
|
|
||||||
print([1])
|
|
||||||
print([2])
|
|
||||||
if rl:
|
|
||||||
rl.unlock()
|
|
||||||
print([3])
|
|
||||||
if wl:
|
|
||||||
wl.unlock()
|
|
||||||
print([4])
|
|
||||||
rl = None
|
|
||||||
print([5])
|
|
||||||
wl = None
|
|
||||||
print([6])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
test()
|
|
|
@ -1,24 +0,0 @@
|
||||||
import sys
|
|
||||||
import rcvs
|
|
||||||
|
|
||||||
def raw_input(prompt):
|
|
||||||
sys.stdout.write(prompt)
|
|
||||||
sys.stdout.flush()
|
|
||||||
return sys.stdin.readline()
|
|
||||||
|
|
||||||
def main():
|
|
||||||
while 1:
|
|
||||||
try:
|
|
||||||
line = input('$ ')
|
|
||||||
except EOFError:
|
|
||||||
break
|
|
||||||
words = line.split()
|
|
||||||
if not words:
|
|
||||||
continue
|
|
||||||
if words[0] != 'rcvs':
|
|
||||||
words.insert(0, 'rcvs')
|
|
||||||
sys.argv = words
|
|
||||||
rcvs.main()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -1,109 +0,0 @@
|
||||||
#! /usr/bin/env python3
|
|
||||||
|
|
||||||
"""Turn a pile of RCS log output into ChangeLog file entries.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
import re
|
|
||||||
import getopt
|
|
||||||
import time
|
|
||||||
|
|
||||||
def main():
|
|
||||||
args = sys.argv[1:]
|
|
||||||
opts, args = getopt.getopt(args, 'p:')
|
|
||||||
prefix = ''
|
|
||||||
for o, a in opts:
|
|
||||||
if p == '-p': prefix = a
|
|
||||||
|
|
||||||
f = sys.stdin
|
|
||||||
allrevs = []
|
|
||||||
while 1:
|
|
||||||
file = getnextfile(f)
|
|
||||||
if not file: break
|
|
||||||
revs = []
|
|
||||||
while 1:
|
|
||||||
rev = getnextrev(f, file)
|
|
||||||
if not rev:
|
|
||||||
break
|
|
||||||
revs.append(rev)
|
|
||||||
if revs:
|
|
||||||
allrevs[len(allrevs):] = revs
|
|
||||||
allrevs.sort()
|
|
||||||
allrevs.reverse()
|
|
||||||
for rev in allrevs:
|
|
||||||
formatrev(rev, prefix)
|
|
||||||
|
|
||||||
parsedateprog = re.compile(
|
|
||||||
'^date: ([0-9]+)/([0-9]+)/([0-9]+) ' +
|
|
||||||
'([0-9]+):([0-9]+):([0-9]+); author: ([^ ;]+)')
|
|
||||||
|
|
||||||
authormap = {
|
|
||||||
'guido': 'Guido van Rossum <guido@cnri.reston.va.us>',
|
|
||||||
'jack': 'Jack Jansen <jack@cwi.nl>',
|
|
||||||
'sjoerd': 'Sjoerd Mullender <sjoerd@cwi.nl>',
|
|
||||||
}
|
|
||||||
|
|
||||||
def formatrev(rev, prefix):
|
|
||||||
dateline, file, revline, log = rev
|
|
||||||
if parsedateprog.match(dateline) >= 0:
|
|
||||||
fields = parsedateprog.group(1, 2, 3, 4, 5, 6)
|
|
||||||
author = parsedateprog.group(7)
|
|
||||||
if author in authormap: author = authormap[author]
|
|
||||||
tfields = list(map(string.atoi, fields)) + [0, 0, 0]
|
|
||||||
tfields[5] = tfields[5] - time.timezone
|
|
||||||
t = time.mktime(tuple(tfields))
|
|
||||||
print(time.ctime(t), '', author)
|
|
||||||
words = string.split(log)
|
|
||||||
words[:0] = ['*', prefix + file + ':']
|
|
||||||
maxcol = 72-8
|
|
||||||
col = maxcol
|
|
||||||
for word in words:
|
|
||||||
if col > 0 and col + len(word) >= maxcol:
|
|
||||||
print()
|
|
||||||
print('\t' + word, end=' ')
|
|
||||||
col = -1
|
|
||||||
else:
|
|
||||||
print(word, end=' ')
|
|
||||||
col = col + 1 + len(word)
|
|
||||||
print()
|
|
||||||
print()
|
|
||||||
|
|
||||||
startprog = re.compile("^Working file: (.*)$")
|
|
||||||
|
|
||||||
def getnextfile(f):
|
|
||||||
while 1:
|
|
||||||
line = f.readline()
|
|
||||||
if not line: return None
|
|
||||||
if startprog.match(line) >= 0:
|
|
||||||
file = startprog.group(1)
|
|
||||||
# Skip until first revision
|
|
||||||
while 1:
|
|
||||||
line = f.readline()
|
|
||||||
if not line: return None
|
|
||||||
if line[:10] == '='*10: return None
|
|
||||||
if line[:10] == '-'*10: break
|
|
||||||
## print "Skipped", line,
|
|
||||||
return file
|
|
||||||
## else:
|
|
||||||
## print "Ignored", line,
|
|
||||||
|
|
||||||
def getnextrev(f, file):
|
|
||||||
# This is called when we are positioned just after a '---' separator
|
|
||||||
revline = f.readline()
|
|
||||||
dateline = f.readline()
|
|
||||||
log = ''
|
|
||||||
while 1:
|
|
||||||
line = f.readline()
|
|
||||||
if not line: break
|
|
||||||
if line[:10] == '='*10:
|
|
||||||
# Ignore the *last* log entry for each file since it
|
|
||||||
# is the revision since which we are logging.
|
|
||||||
return None
|
|
||||||
if line[:10] == '-'*10: break
|
|
||||||
log = log + line
|
|
||||||
return dateline, file, revline, log
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -1,33 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- python -*-
|
|
||||||
#
|
|
||||||
# guido's version, from rcsbump,v 1.2 1995/06/22 21:27:27 bwarsaw Exp
|
|
||||||
#
|
|
||||||
# Python script for bumping up an RCS major revision number.
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import rcslib
|
|
||||||
import string
|
|
||||||
|
|
||||||
WITHLOCK = 1
|
|
||||||
majorrev_re = re.compile('^[0-9]+')
|
|
||||||
|
|
||||||
dir = rcslib.RCS()
|
|
||||||
|
|
||||||
if sys.argv[1:]:
|
|
||||||
files = sys.argv[1:]
|
|
||||||
else:
|
|
||||||
files = dir.listfiles()
|
|
||||||
|
|
||||||
for file in files:
|
|
||||||
# get the major revnumber of the file
|
|
||||||
headbranch = dir.info(file)['head']
|
|
||||||
majorrev_re.match(headbranch)
|
|
||||||
majorrev = string.atoi(majorrev_re.group(0)) + 1
|
|
||||||
|
|
||||||
if not dir.islocked(file):
|
|
||||||
dir.checkout(file, WITHLOCK)
|
|
||||||
|
|
||||||
msg = "Bumping major revision number (to %d)" % majorrev
|
|
||||||
dir.checkin((file, "%s.0" % majorrev), msg, "-f")
|
|
|
@ -1,71 +0,0 @@
|
||||||
"""Customize this file to change the default client etc.
|
|
||||||
|
|
||||||
(In general, it is probably be better to make local operation the
|
|
||||||
default and to require something like an RCSSERVER environment
|
|
||||||
variable to enable remote operation.)
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import string
|
|
||||||
import os
|
|
||||||
|
|
||||||
# These defaults don't belong here -- they should be taken from the
|
|
||||||
# environment or from a hidden file in the current directory
|
|
||||||
|
|
||||||
HOST = 'voorn.cwi.nl'
|
|
||||||
PORT = 4127
|
|
||||||
VERBOSE = 1
|
|
||||||
LOCAL = 0
|
|
||||||
|
|
||||||
import client
|
|
||||||
|
|
||||||
|
|
||||||
class RCSProxyClient(client.SecureClient):
|
|
||||||
|
|
||||||
def __init__(self, address, verbose = client.VERBOSE):
|
|
||||||
client.SecureClient.__init__(self, address, verbose)
|
|
||||||
|
|
||||||
|
|
||||||
def openrcsclient(opts = []):
|
|
||||||
"open an RCSProxy client based on a list of options returned by getopt"
|
|
||||||
import RCSProxy
|
|
||||||
host = HOST
|
|
||||||
port = PORT
|
|
||||||
verbose = VERBOSE
|
|
||||||
local = LOCAL
|
|
||||||
directory = None
|
|
||||||
for o, a in opts:
|
|
||||||
if o == '-h':
|
|
||||||
host = a
|
|
||||||
if ':' in host:
|
|
||||||
i = string.find(host, ':')
|
|
||||||
host, p = host[:i], host[i+1:]
|
|
||||||
if p:
|
|
||||||
port = string.atoi(p)
|
|
||||||
if o == '-p':
|
|
||||||
port = string.atoi(a)
|
|
||||||
if o == '-d':
|
|
||||||
directory = a
|
|
||||||
if o == '-v':
|
|
||||||
verbose = verbose + 1
|
|
||||||
if o == '-q':
|
|
||||||
verbose = 0
|
|
||||||
if o == '-L':
|
|
||||||
local = 1
|
|
||||||
if local:
|
|
||||||
import RCSProxy
|
|
||||||
x = RCSProxy.RCSProxyLocal()
|
|
||||||
else:
|
|
||||||
address = (host, port)
|
|
||||||
x = RCSProxyClient(address, verbose)
|
|
||||||
if not directory:
|
|
||||||
try:
|
|
||||||
directory = open(os.path.join("CVS", "Repository")).readline()
|
|
||||||
except IOError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if directory[-1] == '\n':
|
|
||||||
directory = directory[:-1]
|
|
||||||
if directory:
|
|
||||||
x.cd(directory)
|
|
||||||
return x
|
|
|
@ -1,334 +0,0 @@
|
||||||
"""RCS interface module.
|
|
||||||
|
|
||||||
Defines the class RCS, which represents a directory with rcs version
|
|
||||||
files and (possibly) corresponding work files.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import fnmatch
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import string
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
|
|
||||||
class RCS:
|
|
||||||
|
|
||||||
"""RCS interface class (local filesystem version).
|
|
||||||
|
|
||||||
An instance of this class represents a directory with rcs version
|
|
||||||
files and (possible) corresponding work files.
|
|
||||||
|
|
||||||
Methods provide access to most rcs operations such as
|
|
||||||
checkin/checkout, access to the rcs metadata (revisions, logs,
|
|
||||||
branches etc.) as well as some filesystem operations such as
|
|
||||||
listing all rcs version files.
|
|
||||||
|
|
||||||
XXX BUGS / PROBLEMS
|
|
||||||
|
|
||||||
- The instance always represents the current directory so it's not
|
|
||||||
very useful to have more than one instance around simultaneously
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Characters allowed in work file names
|
|
||||||
okchars = string.ascii_letters + string.digits + '-_=+'
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Constructor."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
"""Destructor."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
# --- Informational methods about a single file/revision ---
|
|
||||||
|
|
||||||
def log(self, name_rev, otherflags = ''):
|
|
||||||
"""Return the full log text for NAME_REV as a string.
|
|
||||||
|
|
||||||
Optional OTHERFLAGS are passed to rlog.
|
|
||||||
|
|
||||||
"""
|
|
||||||
f = self._open(name_rev, 'rlog ' + otherflags)
|
|
||||||
data = f.read()
|
|
||||||
status = self._closepipe(f)
|
|
||||||
if status:
|
|
||||||
data = data + "%s: %s" % status
|
|
||||||
elif data[-1] == '\n':
|
|
||||||
data = data[:-1]
|
|
||||||
return data
|
|
||||||
|
|
||||||
def head(self, name_rev):
|
|
||||||
"""Return the head revision for NAME_REV"""
|
|
||||||
dict = self.info(name_rev)
|
|
||||||
return dict['head']
|
|
||||||
|
|
||||||
def info(self, name_rev):
|
|
||||||
"""Return a dictionary of info (from rlog -h) for NAME_REV
|
|
||||||
|
|
||||||
The dictionary's keys are the keywords that rlog prints
|
|
||||||
(e.g. 'head' and its values are the corresponding data
|
|
||||||
(e.g. '1.3').
|
|
||||||
|
|
||||||
XXX symbolic names and locks are not returned
|
|
||||||
|
|
||||||
"""
|
|
||||||
f = self._open(name_rev, 'rlog -h')
|
|
||||||
dict = {}
|
|
||||||
while 1:
|
|
||||||
line = f.readline()
|
|
||||||
if not line: break
|
|
||||||
if line[0] == '\t':
|
|
||||||
# XXX could be a lock or symbolic name
|
|
||||||
# Anything else?
|
|
||||||
continue
|
|
||||||
i = string.find(line, ':')
|
|
||||||
if i > 0:
|
|
||||||
key, value = line[:i], string.strip(line[i+1:])
|
|
||||||
dict[key] = value
|
|
||||||
status = self._closepipe(f)
|
|
||||||
if status:
|
|
||||||
raise IOError(status)
|
|
||||||
return dict
|
|
||||||
|
|
||||||
# --- Methods that change files ---
|
|
||||||
|
|
||||||
def lock(self, name_rev):
|
|
||||||
"""Set an rcs lock on NAME_REV."""
|
|
||||||
name, rev = self.checkfile(name_rev)
|
|
||||||
cmd = "rcs -l%s %s" % (rev, name)
|
|
||||||
return self._system(cmd)
|
|
||||||
|
|
||||||
def unlock(self, name_rev):
|
|
||||||
"""Clear an rcs lock on NAME_REV."""
|
|
||||||
name, rev = self.checkfile(name_rev)
|
|
||||||
cmd = "rcs -u%s %s" % (rev, name)
|
|
||||||
return self._system(cmd)
|
|
||||||
|
|
||||||
def checkout(self, name_rev, withlock=0, otherflags=""):
|
|
||||||
"""Check out NAME_REV to its work file.
|
|
||||||
|
|
||||||
If optional WITHLOCK is set, check out locked, else unlocked.
|
|
||||||
|
|
||||||
The optional OTHERFLAGS is passed to co without
|
|
||||||
interpretation.
|
|
||||||
|
|
||||||
Any output from co goes to directly to stdout.
|
|
||||||
|
|
||||||
"""
|
|
||||||
name, rev = self.checkfile(name_rev)
|
|
||||||
if withlock: lockflag = "-l"
|
|
||||||
else: lockflag = "-u"
|
|
||||||
cmd = 'co %s%s %s %s' % (lockflag, rev, otherflags, name)
|
|
||||||
return self._system(cmd)
|
|
||||||
|
|
||||||
def checkin(self, name_rev, message=None, otherflags=""):
|
|
||||||
"""Check in NAME_REV from its work file.
|
|
||||||
|
|
||||||
The optional MESSAGE argument becomes the checkin message
|
|
||||||
(default "<none>" if None); or the file description if this is
|
|
||||||
a new file.
|
|
||||||
|
|
||||||
The optional OTHERFLAGS argument is passed to ci without
|
|
||||||
interpretation.
|
|
||||||
|
|
||||||
Any output from ci goes to directly to stdout.
|
|
||||||
|
|
||||||
"""
|
|
||||||
name, rev = self._unmangle(name_rev)
|
|
||||||
new = not self.isvalid(name)
|
|
||||||
if not message: message = "<none>"
|
|
||||||
if message and message[-1] != '\n':
|
|
||||||
message = message + '\n'
|
|
||||||
lockflag = "-u"
|
|
||||||
if new:
|
|
||||||
f = tempfile.NamedTemporaryFile()
|
|
||||||
f.write(message)
|
|
||||||
f.flush()
|
|
||||||
cmd = 'ci %s%s -t%s %s %s' % \
|
|
||||||
(lockflag, rev, f.name, otherflags, name)
|
|
||||||
else:
|
|
||||||
message = re.sub(r'([\"$`])', r'\\\1', message)
|
|
||||||
cmd = 'ci %s%s -m"%s" %s %s' % \
|
|
||||||
(lockflag, rev, message, otherflags, name)
|
|
||||||
return self._system(cmd)
|
|
||||||
|
|
||||||
# --- Exported support methods ---
|
|
||||||
|
|
||||||
def listfiles(self, pat = None):
|
|
||||||
"""Return a list of all version files matching optional PATTERN."""
|
|
||||||
files = os.listdir(os.curdir)
|
|
||||||
files = list(filter(self._isrcs, files))
|
|
||||||
if os.path.isdir('RCS'):
|
|
||||||
files2 = os.listdir('RCS')
|
|
||||||
files2 = list(filter(self._isrcs, files2))
|
|
||||||
files = files + files2
|
|
||||||
files = list(map(self.realname, files))
|
|
||||||
return self._filter(files, pat)
|
|
||||||
|
|
||||||
def isvalid(self, name):
|
|
||||||
"""Test whether NAME has a version file associated."""
|
|
||||||
namev = self.rcsname(name)
|
|
||||||
return (os.path.isfile(namev) or
|
|
||||||
os.path.isfile(os.path.join('RCS', namev)))
|
|
||||||
|
|
||||||
def rcsname(self, name):
|
|
||||||
"""Return the pathname of the version file for NAME.
|
|
||||||
|
|
||||||
The argument can be a work file name or a version file name.
|
|
||||||
If the version file does not exist, the name of the version
|
|
||||||
file that would be created by "ci" is returned.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if self._isrcs(name): namev = name
|
|
||||||
else: namev = name + ',v'
|
|
||||||
if os.path.isfile(namev): return namev
|
|
||||||
namev = os.path.join('RCS', os.path.basename(namev))
|
|
||||||
if os.path.isfile(namev): return namev
|
|
||||||
if os.path.isdir('RCS'):
|
|
||||||
return os.path.join('RCS', namev)
|
|
||||||
else:
|
|
||||||
return namev
|
|
||||||
|
|
||||||
def realname(self, namev):
|
|
||||||
"""Return the pathname of the work file for NAME.
|
|
||||||
|
|
||||||
The argument can be a work file name or a version file name.
|
|
||||||
If the work file does not exist, the name of the work file
|
|
||||||
that would be created by "co" is returned.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if self._isrcs(namev): name = namev[:-2]
|
|
||||||
else: name = namev
|
|
||||||
if os.path.isfile(name): return name
|
|
||||||
name = os.path.basename(name)
|
|
||||||
return name
|
|
||||||
|
|
||||||
def islocked(self, name_rev):
|
|
||||||
"""Test whether FILE (which must have a version file) is locked.
|
|
||||||
|
|
||||||
XXX This does not tell you which revision number is locked and
|
|
||||||
ignores any revision you may pass in (by virtue of using rlog
|
|
||||||
-L -R).
|
|
||||||
|
|
||||||
"""
|
|
||||||
f = self._open(name_rev, 'rlog -L -R')
|
|
||||||
line = f.readline()
|
|
||||||
status = self._closepipe(f)
|
|
||||||
if status:
|
|
||||||
raise IOError(status)
|
|
||||||
if not line: return None
|
|
||||||
if line[-1] == '\n':
|
|
||||||
line = line[:-1]
|
|
||||||
return self.realname(name_rev) == self.realname(line)
|
|
||||||
|
|
||||||
def checkfile(self, name_rev):
|
|
||||||
"""Normalize NAME_REV into a (NAME, REV) tuple.
|
|
||||||
|
|
||||||
Raise an exception if there is no corresponding version file.
|
|
||||||
|
|
||||||
"""
|
|
||||||
name, rev = self._unmangle(name_rev)
|
|
||||||
if not self.isvalid(name):
|
|
||||||
raise os.error('not an rcs file %r' % (name,))
|
|
||||||
return name, rev
|
|
||||||
|
|
||||||
# --- Internal methods ---
|
|
||||||
|
|
||||||
def _open(self, name_rev, cmd = 'co -p', rflag = '-r'):
|
|
||||||
"""INTERNAL: open a read pipe to NAME_REV using optional COMMAND.
|
|
||||||
|
|
||||||
Optional FLAG is used to indicate the revision (default -r).
|
|
||||||
|
|
||||||
Default COMMAND is "co -p".
|
|
||||||
|
|
||||||
Return a file object connected by a pipe to the command's
|
|
||||||
output.
|
|
||||||
|
|
||||||
"""
|
|
||||||
name, rev = self.checkfile(name_rev)
|
|
||||||
namev = self.rcsname(name)
|
|
||||||
if rev:
|
|
||||||
cmd = cmd + ' ' + rflag + rev
|
|
||||||
return os.popen("%s %r" % (cmd, namev))
|
|
||||||
|
|
||||||
def _unmangle(self, name_rev):
|
|
||||||
"""INTERNAL: Normalize NAME_REV argument to (NAME, REV) tuple.
|
|
||||||
|
|
||||||
Raise an exception if NAME contains invalid characters.
|
|
||||||
|
|
||||||
A NAME_REV argument is either NAME string (implying REV='') or
|
|
||||||
a tuple of the form (NAME, REV).
|
|
||||||
|
|
||||||
"""
|
|
||||||
if type(name_rev) == type(''):
|
|
||||||
name_rev = name, rev = name_rev, ''
|
|
||||||
else:
|
|
||||||
name, rev = name_rev
|
|
||||||
for c in rev:
|
|
||||||
if c not in self.okchars:
|
|
||||||
raise ValueError("bad char in rev")
|
|
||||||
return name_rev
|
|
||||||
|
|
||||||
def _closepipe(self, f):
|
|
||||||
"""INTERNAL: Close PIPE and print its exit status if nonzero."""
|
|
||||||
sts = f.close()
|
|
||||||
if not sts: return None
|
|
||||||
detail, reason = divmod(sts, 256)
|
|
||||||
if reason == 0: return 'exit', detail # Exit status
|
|
||||||
signal = reason&0x7F
|
|
||||||
if signal == 0x7F:
|
|
||||||
code = 'stopped'
|
|
||||||
signal = detail
|
|
||||||
else:
|
|
||||||
code = 'killed'
|
|
||||||
if reason&0x80:
|
|
||||||
code = code + '(coredump)'
|
|
||||||
return code, signal
|
|
||||||
|
|
||||||
def _system(self, cmd):
|
|
||||||
"""INTERNAL: run COMMAND in a subshell.
|
|
||||||
|
|
||||||
Standard input for the command is taken from /dev/null.
|
|
||||||
|
|
||||||
Raise IOError when the exit status is not zero.
|
|
||||||
|
|
||||||
Return whatever the calling method should return; normally
|
|
||||||
None.
|
|
||||||
|
|
||||||
A derived class may override this method and redefine it to
|
|
||||||
capture stdout/stderr of the command and return it.
|
|
||||||
|
|
||||||
"""
|
|
||||||
cmd = cmd + " </dev/null"
|
|
||||||
sts = os.system(cmd)
|
|
||||||
if sts: raise IOError("command exit status %d" % sts)
|
|
||||||
|
|
||||||
def _filter(self, files, pat = None):
|
|
||||||
"""INTERNAL: Return a sorted copy of the given list of FILES.
|
|
||||||
|
|
||||||
If a second PATTERN argument is given, only files matching it
|
|
||||||
are kept. No check for valid filenames is made.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if pat:
|
|
||||||
def keep(name, pat = pat):
|
|
||||||
return fnmatch.fnmatch(name, pat)
|
|
||||||
files = list(filter(keep, files))
|
|
||||||
else:
|
|
||||||
files = files[:]
|
|
||||||
files.sort()
|
|
||||||
return files
|
|
||||||
|
|
||||||
def _remove(self, fn):
|
|
||||||
"""INTERNAL: remove FILE without complaints."""
|
|
||||||
try:
|
|
||||||
os.unlink(fn)
|
|
||||||
except os.error:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _isrcs(self, name):
|
|
||||||
"""INTERNAL: Test whether NAME ends in ',v'."""
|
|
||||||
return name[-2:] == ',v'
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import addpack
|
|
||||||
addpack.addpack('/home/guido/src/python/Demo/pdist')
|
|
||||||
|
|
||||||
import rcvs
|
|
||||||
|
|
||||||
rcvs.main()
|
|
|
@ -1,476 +0,0 @@
|
||||||
#! /usr/bin/env python3
|
|
||||||
|
|
||||||
"""Remote CVS -- command line interface"""
|
|
||||||
|
|
||||||
# XXX To do:
|
|
||||||
#
|
|
||||||
# Bugs:
|
|
||||||
# - if the remote file is deleted, "rcvs update" will fail
|
|
||||||
#
|
|
||||||
# Functionality:
|
|
||||||
# - cvs rm
|
|
||||||
# - descend into directories (alraedy done for update)
|
|
||||||
# - conflict resolution
|
|
||||||
# - other relevant commands?
|
|
||||||
# - branches
|
|
||||||
#
|
|
||||||
# - Finesses:
|
|
||||||
# - retain file mode's x bits
|
|
||||||
# - complain when "nothing known about filename"
|
|
||||||
# - edit log message the way CVS lets you edit it
|
|
||||||
# - cvs diff -rREVA -rREVB
|
|
||||||
# - send mail the way CVS sends it
|
|
||||||
#
|
|
||||||
# Performance:
|
|
||||||
# - cache remote checksums (for every revision ever seen!)
|
|
||||||
# - translate symbolic revisions to numeric revisions
|
|
||||||
#
|
|
||||||
# Reliability:
|
|
||||||
# - remote locking
|
|
||||||
#
|
|
||||||
# Security:
|
|
||||||
# - Authenticated RPC?
|
|
||||||
|
|
||||||
|
|
||||||
from cvslib import CVS, File
|
|
||||||
import md5
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from cmdfw import CommandFrameWork
|
|
||||||
|
|
||||||
|
|
||||||
DEF_LOCAL = 1 # Default -l
|
|
||||||
|
|
||||||
|
|
||||||
class MyFile(File):
|
|
||||||
|
|
||||||
def action(self):
|
|
||||||
"""Return a code indicating the update status of this file.
|
|
||||||
|
|
||||||
The possible return values are:
|
|
||||||
|
|
||||||
'=' -- everything's fine
|
|
||||||
'0' -- file doesn't exist anywhere
|
|
||||||
'?' -- exists locally only
|
|
||||||
'A' -- new locally
|
|
||||||
'R' -- deleted locally
|
|
||||||
'U' -- changed remotely, no changes locally
|
|
||||||
(includes new remotely or deleted remotely)
|
|
||||||
'M' -- changed locally, no changes remotely
|
|
||||||
'C' -- conflict: changed locally as well as remotely
|
|
||||||
(includes cases where the file has been added
|
|
||||||
or removed locally and remotely)
|
|
||||||
'D' -- deleted remotely
|
|
||||||
'N' -- new remotely
|
|
||||||
'r' -- get rid of entry
|
|
||||||
'c' -- create entry
|
|
||||||
'u' -- update entry
|
|
||||||
|
|
||||||
(and probably others :-)
|
|
||||||
"""
|
|
||||||
if not self.lseen:
|
|
||||||
self.getlocal()
|
|
||||||
if not self.rseen:
|
|
||||||
self.getremote()
|
|
||||||
if not self.eseen:
|
|
||||||
if not self.lsum:
|
|
||||||
if not self.rsum: return '0' # Never heard of
|
|
||||||
else:
|
|
||||||
return 'N' # New remotely
|
|
||||||
else: # self.lsum
|
|
||||||
if not self.rsum: return '?' # Local only
|
|
||||||
# Local and remote, but no entry
|
|
||||||
if self.lsum == self.rsum:
|
|
||||||
return 'c' # Restore entry only
|
|
||||||
else: return 'C' # Real conflict
|
|
||||||
else: # self.eseen
|
|
||||||
if not self.lsum:
|
|
||||||
if self.edeleted:
|
|
||||||
if self.rsum: return 'R' # Removed
|
|
||||||
else: return 'r' # Get rid of entry
|
|
||||||
else: # not self.edeleted
|
|
||||||
if self.rsum:
|
|
||||||
print("warning:", end=' ')
|
|
||||||
print(self.file, end=' ')
|
|
||||||
print("was lost")
|
|
||||||
return 'U'
|
|
||||||
else: return 'r' # Get rid of entry
|
|
||||||
else: # self.lsum
|
|
||||||
if not self.rsum:
|
|
||||||
if self.enew: return 'A' # New locally
|
|
||||||
else: return 'D' # Deleted remotely
|
|
||||||
else: # self.rsum
|
|
||||||
if self.enew:
|
|
||||||
if self.lsum == self.rsum:
|
|
||||||
return 'u'
|
|
||||||
else:
|
|
||||||
return 'C'
|
|
||||||
if self.lsum == self.esum:
|
|
||||||
if self.esum == self.rsum:
|
|
||||||
return '='
|
|
||||||
else:
|
|
||||||
return 'U'
|
|
||||||
elif self.esum == self.rsum:
|
|
||||||
return 'M'
|
|
||||||
elif self.lsum == self.rsum:
|
|
||||||
return 'u'
|
|
||||||
else:
|
|
||||||
return 'C'
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
code = self.action()
|
|
||||||
if code == '=': return
|
|
||||||
print(code, self.file)
|
|
||||||
if code in ('U', 'N'):
|
|
||||||
self.get()
|
|
||||||
elif code == 'C':
|
|
||||||
print("%s: conflict resolution not yet implemented" % \
|
|
||||||
self.file)
|
|
||||||
elif code == 'D':
|
|
||||||
remove(self.file)
|
|
||||||
self.eseen = 0
|
|
||||||
elif code == 'r':
|
|
||||||
self.eseen = 0
|
|
||||||
elif code in ('c', 'u'):
|
|
||||||
self.eseen = 1
|
|
||||||
self.erev = self.rrev
|
|
||||||
self.enew = 0
|
|
||||||
self.edeleted = 0
|
|
||||||
self.esum = self.rsum
|
|
||||||
self.emtime, self.ectime = os.stat(self.file)[-2:]
|
|
||||||
self.extra = ''
|
|
||||||
|
|
||||||
def commit(self, message = ""):
|
|
||||||
code = self.action()
|
|
||||||
if code in ('A', 'M'):
|
|
||||||
self.put(message)
|
|
||||||
return 1
|
|
||||||
elif code == 'R':
|
|
||||||
print("%s: committing removes not yet implemented" % \
|
|
||||||
self.file)
|
|
||||||
elif code == 'C':
|
|
||||||
print("%s: conflict resolution not yet implemented" % \
|
|
||||||
self.file)
|
|
||||||
|
|
||||||
def diff(self, opts = []):
|
|
||||||
self.action() # To update lseen, rseen
|
|
||||||
flags = ''
|
|
||||||
rev = self.rrev
|
|
||||||
# XXX should support two rev options too!
|
|
||||||
for o, a in opts:
|
|
||||||
if o == '-r':
|
|
||||||
rev = a
|
|
||||||
else:
|
|
||||||
flags = flags + ' ' + o + a
|
|
||||||
if rev == self.rrev and self.lsum == self.rsum:
|
|
||||||
return
|
|
||||||
flags = flags[1:]
|
|
||||||
fn = self.file
|
|
||||||
data = self.proxy.get((fn, rev))
|
|
||||||
sum = md5.new(data).digest()
|
|
||||||
if self.lsum == sum:
|
|
||||||
return
|
|
||||||
import tempfile
|
|
||||||
tf = tempfile.NamedTemporaryFile()
|
|
||||||
tf.write(data)
|
|
||||||
tf.flush()
|
|
||||||
print('diff %s -r%s %s' % (flags, rev, fn))
|
|
||||||
sts = os.system('diff %s %s %s' % (flags, tf.name, fn))
|
|
||||||
if sts:
|
|
||||||
print('='*70)
|
|
||||||
|
|
||||||
def commitcheck(self):
|
|
||||||
return self.action() != 'C'
|
|
||||||
|
|
||||||
def put(self, message = ""):
|
|
||||||
print("Checking in", self.file, "...")
|
|
||||||
data = open(self.file).read()
|
|
||||||
if not self.enew:
|
|
||||||
self.proxy.lock(self.file)
|
|
||||||
messages = self.proxy.put(self.file, data, message)
|
|
||||||
if messages:
|
|
||||||
print(messages)
|
|
||||||
self.setentry(self.proxy.head(self.file), self.lsum)
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
data = self.proxy.get(self.file)
|
|
||||||
f = open(self.file, 'w')
|
|
||||||
f.write(data)
|
|
||||||
f.close()
|
|
||||||
self.setentry(self.rrev, self.rsum)
|
|
||||||
|
|
||||||
def log(self, otherflags):
|
|
||||||
print(self.proxy.log(self.file, otherflags))
|
|
||||||
|
|
||||||
def add(self):
|
|
||||||
self.eseen = 0 # While we're hacking...
|
|
||||||
self.esum = self.lsum
|
|
||||||
self.emtime, self.ectime = 0, 0
|
|
||||||
self.erev = ''
|
|
||||||
self.enew = 1
|
|
||||||
self.edeleted = 0
|
|
||||||
self.eseen = 1 # Done
|
|
||||||
self.extra = ''
|
|
||||||
|
|
||||||
def setentry(self, erev, esum):
|
|
||||||
self.eseen = 0 # While we're hacking...
|
|
||||||
self.esum = esum
|
|
||||||
self.emtime, self.ectime = os.stat(self.file)[-2:]
|
|
||||||
self.erev = erev
|
|
||||||
self.enew = 0
|
|
||||||
self.edeleted = 0
|
|
||||||
self.eseen = 1 # Done
|
|
||||||
self.extra = ''
|
|
||||||
|
|
||||||
|
|
||||||
SENDMAIL = "/usr/lib/sendmail -t"
|
|
||||||
MAILFORM = """To: %s
|
|
||||||
Subject: CVS changes: %s
|
|
||||||
|
|
||||||
...Message from rcvs...
|
|
||||||
|
|
||||||
Committed files:
|
|
||||||
%s
|
|
||||||
|
|
||||||
Log message:
|
|
||||||
%s
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class RCVS(CVS):
|
|
||||||
|
|
||||||
FileClass = MyFile
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
CVS.__init__(self)
|
|
||||||
|
|
||||||
def update(self, files):
|
|
||||||
for e in self.whichentries(files, 1):
|
|
||||||
e.update()
|
|
||||||
|
|
||||||
def commit(self, files, message = ""):
|
|
||||||
list = self.whichentries(files)
|
|
||||||
if not list: return
|
|
||||||
ok = 1
|
|
||||||
for e in list:
|
|
||||||
if not e.commitcheck():
|
|
||||||
ok = 0
|
|
||||||
if not ok:
|
|
||||||
print("correct above errors first")
|
|
||||||
return
|
|
||||||
if not message:
|
|
||||||
message = input("One-liner: ")
|
|
||||||
committed = []
|
|
||||||
for e in list:
|
|
||||||
if e.commit(message):
|
|
||||||
committed.append(e.file)
|
|
||||||
self.mailinfo(committed, message)
|
|
||||||
|
|
||||||
def mailinfo(self, files, message = ""):
|
|
||||||
towhom = "sjoerd@cwi.nl, jack@cwi.nl" # XXX
|
|
||||||
mailtext = MAILFORM % (towhom, ' '.join(files),
|
|
||||||
' '.join(files), message)
|
|
||||||
print('-'*70)
|
|
||||||
print(mailtext)
|
|
||||||
print('-'*70)
|
|
||||||
ok = input("OK to mail to %s? " % towhom)
|
|
||||||
if ok.lower().strip() in ('y', 'ye', 'yes'):
|
|
||||||
p = os.popen(SENDMAIL, "w")
|
|
||||||
p.write(mailtext)
|
|
||||||
sts = p.close()
|
|
||||||
if sts:
|
|
||||||
print("Sendmail exit status %s" % str(sts))
|
|
||||||
else:
|
|
||||||
print("Mail sent.")
|
|
||||||
else:
|
|
||||||
print("No mail sent.")
|
|
||||||
|
|
||||||
def report(self, files):
|
|
||||||
for e in self.whichentries(files):
|
|
||||||
e.report()
|
|
||||||
|
|
||||||
def diff(self, files, opts):
|
|
||||||
for e in self.whichentries(files):
|
|
||||||
e.diff(opts)
|
|
||||||
|
|
||||||
def add(self, files):
|
|
||||||
if not files:
|
|
||||||
raise RuntimeError("'cvs add' needs at least one file")
|
|
||||||
list = []
|
|
||||||
for e in self.whichentries(files, 1):
|
|
||||||
e.add()
|
|
||||||
|
|
||||||
def rm(self, files):
|
|
||||||
if not files:
|
|
||||||
raise RuntimeError("'cvs rm' needs at least one file")
|
|
||||||
raise RuntimeError("'cvs rm' not yet imlemented")
|
|
||||||
|
|
||||||
def log(self, files, opts):
|
|
||||||
flags = ''
|
|
||||||
for o, a in opts:
|
|
||||||
flags = flags + ' ' + o + a
|
|
||||||
for e in self.whichentries(files):
|
|
||||||
e.log(flags)
|
|
||||||
|
|
||||||
def whichentries(self, files, localfilestoo = 0):
|
|
||||||
if files:
|
|
||||||
list = []
|
|
||||||
for file in files:
|
|
||||||
if file in self.entries:
|
|
||||||
e = self.entries[file]
|
|
||||||
else:
|
|
||||||
e = self.FileClass(file)
|
|
||||||
self.entries[file] = e
|
|
||||||
list.append(e)
|
|
||||||
else:
|
|
||||||
list = list(self.entries.values())
|
|
||||||
for file in self.proxy.listfiles():
|
|
||||||
if file in self.entries:
|
|
||||||
continue
|
|
||||||
e = self.FileClass(file)
|
|
||||||
self.entries[file] = e
|
|
||||||
list.append(e)
|
|
||||||
if localfilestoo:
|
|
||||||
for file in os.listdir(os.curdir):
|
|
||||||
if file not in self.entries \
|
|
||||||
and not self.ignored(file):
|
|
||||||
e = self.FileClass(file)
|
|
||||||
self.entries[file] = e
|
|
||||||
list.append(e)
|
|
||||||
list.sort()
|
|
||||||
if self.proxy:
|
|
||||||
for e in list:
|
|
||||||
if e.proxy is None:
|
|
||||||
e.proxy = self.proxy
|
|
||||||
return list
|
|
||||||
|
|
||||||
|
|
||||||
class rcvs(CommandFrameWork):
|
|
||||||
|
|
||||||
GlobalFlags = 'd:h:p:qvL'
|
|
||||||
UsageMessage = \
|
|
||||||
"usage: rcvs [-d directory] [-h host] [-p port] [-q] [-v] [subcommand arg ...]"
|
|
||||||
PostUsageMessage = \
|
|
||||||
"If no subcommand is given, the status of all files is listed"
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Constructor."""
|
|
||||||
CommandFrameWork.__init__(self)
|
|
||||||
self.proxy = None
|
|
||||||
self.cvs = RCVS()
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self.proxy:
|
|
||||||
self.proxy._close()
|
|
||||||
self.proxy = None
|
|
||||||
|
|
||||||
def recurse(self):
|
|
||||||
self.close()
|
|
||||||
names = os.listdir(os.curdir)
|
|
||||||
for name in names:
|
|
||||||
if name == os.curdir or name == os.pardir:
|
|
||||||
continue
|
|
||||||
if name == "CVS":
|
|
||||||
continue
|
|
||||||
if not os.path.isdir(name):
|
|
||||||
continue
|
|
||||||
if os.path.islink(name):
|
|
||||||
continue
|
|
||||||
print("--- entering subdirectory", name, "---")
|
|
||||||
os.chdir(name)
|
|
||||||
try:
|
|
||||||
if os.path.isdir("CVS"):
|
|
||||||
self.__class__().run()
|
|
||||||
else:
|
|
||||||
self.recurse()
|
|
||||||
finally:
|
|
||||||
os.chdir(os.pardir)
|
|
||||||
print("--- left subdirectory", name, "---")
|
|
||||||
|
|
||||||
def options(self, opts):
|
|
||||||
self.opts = opts
|
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
import rcsclient
|
|
||||||
self.proxy = rcsclient.openrcsclient(self.opts)
|
|
||||||
self.cvs.setproxy(self.proxy)
|
|
||||||
self.cvs.getentries()
|
|
||||||
|
|
||||||
def default(self):
|
|
||||||
self.cvs.report([])
|
|
||||||
|
|
||||||
def do_report(self, opts, files):
|
|
||||||
self.cvs.report(files)
|
|
||||||
|
|
||||||
def do_update(self, opts, files):
|
|
||||||
"""update [-l] [-R] [file] ..."""
|
|
||||||
local = DEF_LOCAL
|
|
||||||
for o, a in opts:
|
|
||||||
if o == '-l': local = 1
|
|
||||||
if o == '-R': local = 0
|
|
||||||
self.cvs.update(files)
|
|
||||||
self.cvs.putentries()
|
|
||||||
if not local and not files:
|
|
||||||
self.recurse()
|
|
||||||
flags_update = '-lR'
|
|
||||||
do_up = do_update
|
|
||||||
flags_up = flags_update
|
|
||||||
|
|
||||||
def do_commit(self, opts, files):
|
|
||||||
"""commit [-m message] [file] ..."""
|
|
||||||
message = ""
|
|
||||||
for o, a in opts:
|
|
||||||
if o == '-m': message = a
|
|
||||||
self.cvs.commit(files, message)
|
|
||||||
self.cvs.putentries()
|
|
||||||
flags_commit = 'm:'
|
|
||||||
do_com = do_commit
|
|
||||||
flags_com = flags_commit
|
|
||||||
|
|
||||||
def do_diff(self, opts, files):
|
|
||||||
"""diff [difflags] [file] ..."""
|
|
||||||
self.cvs.diff(files, opts)
|
|
||||||
flags_diff = 'cbitwcefhnlr:sD:S:'
|
|
||||||
do_dif = do_diff
|
|
||||||
flags_dif = flags_diff
|
|
||||||
|
|
||||||
def do_add(self, opts, files):
|
|
||||||
"""add file ..."""
|
|
||||||
if not files:
|
|
||||||
print("'rcvs add' requires at least one file")
|
|
||||||
return
|
|
||||||
self.cvs.add(files)
|
|
||||||
self.cvs.putentries()
|
|
||||||
|
|
||||||
def do_remove(self, opts, files):
|
|
||||||
"""remove file ..."""
|
|
||||||
if not files:
|
|
||||||
print("'rcvs remove' requires at least one file")
|
|
||||||
return
|
|
||||||
self.cvs.remove(files)
|
|
||||||
self.cvs.putentries()
|
|
||||||
do_rm = do_remove
|
|
||||||
|
|
||||||
def do_log(self, opts, files):
|
|
||||||
"""log [rlog-options] [file] ..."""
|
|
||||||
self.cvs.log(files, opts)
|
|
||||||
flags_log = 'bhLNRtd:s:V:r:'
|
|
||||||
|
|
||||||
|
|
||||||
def remove(fn):
|
|
||||||
try:
|
|
||||||
os.unlink(fn)
|
|
||||||
except os.error:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
r = rcvs()
|
|
||||||
try:
|
|
||||||
r.run()
|
|
||||||
finally:
|
|
||||||
r.close()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import addpack
|
|
||||||
addpack.addpack('/home/guido/src/python/Demo/pdist')
|
|
||||||
|
|
||||||
import rrcs
|
|
||||||
|
|
||||||
rrcs.main()
|
|
|
@ -1,158 +0,0 @@
|
||||||
#! /usr/bin/env python3
|
|
||||||
|
|
||||||
"Remote RCS -- command line interface"
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import getopt
|
|
||||||
import string
|
|
||||||
import md5
|
|
||||||
import tempfile
|
|
||||||
from rcsclient import openrcsclient
|
|
||||||
|
|
||||||
def main():
|
|
||||||
sys.stdout = sys.stderr
|
|
||||||
try:
|
|
||||||
opts, rest = getopt.getopt(sys.argv[1:], 'h:p:d:qvL')
|
|
||||||
if not rest:
|
|
||||||
cmd = 'head'
|
|
||||||
else:
|
|
||||||
cmd, rest = rest[0], rest[1:]
|
|
||||||
if cmd not in commands:
|
|
||||||
raise getopt.error("unknown command")
|
|
||||||
coptset, func = commands[cmd]
|
|
||||||
copts, files = getopt.getopt(rest, coptset)
|
|
||||||
except getopt.error as msg:
|
|
||||||
print(msg)
|
|
||||||
print("usage: rrcs [options] command [options] [file] ...")
|
|
||||||
print("where command can be:")
|
|
||||||
print(" ci|put # checkin the given files")
|
|
||||||
print(" co|get # checkout")
|
|
||||||
print(" info # print header info")
|
|
||||||
print(" head # print revision of head branch")
|
|
||||||
print(" list # list filename if valid")
|
|
||||||
print(" log # print full log")
|
|
||||||
print(" diff # diff rcs file and work file")
|
|
||||||
print("if no files are given, all remote rcs files are assumed")
|
|
||||||
sys.exit(2)
|
|
||||||
x = openrcsclient(opts)
|
|
||||||
if not files:
|
|
||||||
files = x.listfiles()
|
|
||||||
for fn in files:
|
|
||||||
try:
|
|
||||||
func(x, copts, fn)
|
|
||||||
except (IOError, os.error) as msg:
|
|
||||||
print("%s: %s" % (fn, msg))
|
|
||||||
|
|
||||||
def checkin(x, copts, fn):
|
|
||||||
f = open(fn)
|
|
||||||
data = f.read()
|
|
||||||
f.close()
|
|
||||||
new = not x.isvalid(fn)
|
|
||||||
if not new and same(x, copts, fn, data):
|
|
||||||
print("%s: unchanged since last checkin" % fn)
|
|
||||||
return
|
|
||||||
print("Checking in", fn, "...")
|
|
||||||
message = asklogmessage(new)
|
|
||||||
messages = x.put(fn, data, message)
|
|
||||||
if messages:
|
|
||||||
print(messages)
|
|
||||||
|
|
||||||
def checkout(x, copts, fn):
|
|
||||||
data = x.get(fn)
|
|
||||||
f = open(fn, 'w')
|
|
||||||
f.write(data)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def lock(x, copts, fn):
|
|
||||||
x.lock(fn)
|
|
||||||
|
|
||||||
def unlock(x, copts, fn):
|
|
||||||
x.unlock(fn)
|
|
||||||
|
|
||||||
def info(x, copts, fn):
|
|
||||||
info_dict = x.info(fn)
|
|
||||||
for key in sorted(info_dict.keys()):
|
|
||||||
print(key + ':', info_dict[key])
|
|
||||||
print('='*70)
|
|
||||||
|
|
||||||
def head(x, copts, fn):
|
|
||||||
head = x.head(fn)
|
|
||||||
print(fn, head)
|
|
||||||
|
|
||||||
def list(x, copts, fn):
|
|
||||||
if x.isvalid(fn):
|
|
||||||
print(fn)
|
|
||||||
|
|
||||||
def log(x, copts, fn):
|
|
||||||
flags = ''
|
|
||||||
for o, a in copts:
|
|
||||||
flags = flags + ' ' + o + a
|
|
||||||
flags = flags[1:]
|
|
||||||
messages = x.log(fn, flags)
|
|
||||||
print(messages)
|
|
||||||
|
|
||||||
def diff(x, copts, fn):
|
|
||||||
if same(x, copts, fn):
|
|
||||||
return
|
|
||||||
flags = ''
|
|
||||||
for o, a in copts:
|
|
||||||
flags = flags + ' ' + o + a
|
|
||||||
flags = flags[1:]
|
|
||||||
data = x.get(fn)
|
|
||||||
tf = tempfile.NamedTemporaryFile()
|
|
||||||
tf.write(data)
|
|
||||||
tf.flush()
|
|
||||||
print('diff %s -r%s %s' % (flags, x.head(fn), fn))
|
|
||||||
sts = os.system('diff %s %s %s' % (flags, tf.name, fn))
|
|
||||||
if sts:
|
|
||||||
print('='*70)
|
|
||||||
|
|
||||||
def same(x, copts, fn, data = None):
|
|
||||||
if data is None:
|
|
||||||
f = open(fn)
|
|
||||||
data = f.read()
|
|
||||||
f.close()
|
|
||||||
lsum = md5.new(data).digest()
|
|
||||||
rsum = x.sum(fn)
|
|
||||||
return lsum == rsum
|
|
||||||
|
|
||||||
def asklogmessage(new):
|
|
||||||
if new:
|
|
||||||
print("enter description,", end=' ')
|
|
||||||
else:
|
|
||||||
print("enter log message,", end=' ')
|
|
||||||
print("terminate with single '.' or end of file:")
|
|
||||||
if new:
|
|
||||||
print("NOTE: This is NOT the log message!")
|
|
||||||
message = ""
|
|
||||||
while 1:
|
|
||||||
sys.stderr.write(">> ")
|
|
||||||
sys.stderr.flush()
|
|
||||||
line = sys.stdin.readline()
|
|
||||||
if not line or line == '.\n': break
|
|
||||||
message = message + line
|
|
||||||
return message
|
|
||||||
|
|
||||||
def remove(fn):
|
|
||||||
try:
|
|
||||||
os.unlink(fn)
|
|
||||||
except os.error:
|
|
||||||
pass
|
|
||||||
|
|
||||||
commands = {
|
|
||||||
'ci': ('', checkin),
|
|
||||||
'put': ('', checkin),
|
|
||||||
'co': ('', checkout),
|
|
||||||
'get': ('', checkout),
|
|
||||||
'info': ('', info),
|
|
||||||
'head': ('', head),
|
|
||||||
'list': ('', list),
|
|
||||||
'lock': ('', lock),
|
|
||||||
'unlock': ('', unlock),
|
|
||||||
'log': ('bhLRtd:l:r:s:w:V:', log),
|
|
||||||
'diff': ('c', diff),
|
|
||||||
}
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -1,33 +0,0 @@
|
||||||
class Security:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
import os
|
|
||||||
env = os.environ
|
|
||||||
if 'PYTHON_KEYFILE' in env:
|
|
||||||
keyfile = env['PYTHON_KEYFILE']
|
|
||||||
else:
|
|
||||||
keyfile = '.python_keyfile'
|
|
||||||
if 'HOME' in env:
|
|
||||||
keyfile = os.path.join(env['HOME'], keyfile)
|
|
||||||
if not os.path.exists(keyfile):
|
|
||||||
import sys
|
|
||||||
for dir in sys.path:
|
|
||||||
kf = os.path.join(dir, keyfile)
|
|
||||||
if os.path.exists(kf):
|
|
||||||
keyfile = kf
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
self._key = eval(open(keyfile).readline())
|
|
||||||
except IOError:
|
|
||||||
raise IOError("python keyfile %s: cannot open" % keyfile)
|
|
||||||
|
|
||||||
def _generate_challenge(self):
|
|
||||||
import random
|
|
||||||
return random.randint(100, 100000)
|
|
||||||
|
|
||||||
def _compare_challenge_response(self, challenge, response):
|
|
||||||
return self._encode_challenge(challenge) == response
|
|
||||||
|
|
||||||
def _encode_challenge(self, challenge):
|
|
||||||
p, m = self._key
|
|
||||||
return pow(int(challenge), p, m)
|
|
|
@ -1,143 +0,0 @@
|
||||||
"""RPC Server module."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import socket
|
|
||||||
import pickle
|
|
||||||
from fnmatch import fnmatch
|
|
||||||
from reprlib import repr
|
|
||||||
|
|
||||||
|
|
||||||
# Default verbosity (0 = silent, 1 = print connections, 2 = print requests too)
|
|
||||||
VERBOSE = 1
|
|
||||||
|
|
||||||
|
|
||||||
class Server:
|
|
||||||
|
|
||||||
"""RPC Server class. Derive a class to implement a particular service."""
|
|
||||||
|
|
||||||
def __init__(self, address, verbose = VERBOSE):
|
|
||||||
if type(address) == type(0):
|
|
||||||
address = ('', address)
|
|
||||||
self._address = address
|
|
||||||
self._verbose = verbose
|
|
||||||
self._socket = None
|
|
||||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
self._socket.bind(address)
|
|
||||||
self._socket.listen(1)
|
|
||||||
self._listening = 1
|
|
||||||
|
|
||||||
def _setverbose(self, verbose):
|
|
||||||
self._verbose = verbose
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self._close()
|
|
||||||
|
|
||||||
def _close(self):
|
|
||||||
self._listening = 0
|
|
||||||
if self._socket:
|
|
||||||
self._socket.close()
|
|
||||||
self._socket = None
|
|
||||||
|
|
||||||
def _serverloop(self):
|
|
||||||
while self._listening:
|
|
||||||
self._serve()
|
|
||||||
|
|
||||||
def _serve(self):
|
|
||||||
if self._verbose: print("Wait for connection ...")
|
|
||||||
conn, address = self._socket.accept()
|
|
||||||
if self._verbose: print("Accepted connection from %s" % repr(address))
|
|
||||||
if not self._verify(conn, address):
|
|
||||||
print("*** Connection from %s refused" % repr(address))
|
|
||||||
conn.close()
|
|
||||||
return
|
|
||||||
rf = conn.makefile('r')
|
|
||||||
wf = conn.makefile('w')
|
|
||||||
ok = 1
|
|
||||||
while ok:
|
|
||||||
wf.flush()
|
|
||||||
if self._verbose > 1: print("Wait for next request ...")
|
|
||||||
ok = self._dorequest(rf, wf)
|
|
||||||
|
|
||||||
_valid = ['192.16.201.*', '192.16.197.*', '132.151.1.*', '129.6.64.*']
|
|
||||||
|
|
||||||
def _verify(self, conn, address):
|
|
||||||
host, port = address
|
|
||||||
for pat in self._valid:
|
|
||||||
if fnmatch(host, pat): return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def _dorequest(self, rf, wf):
|
|
||||||
rp = pickle.Unpickler(rf)
|
|
||||||
try:
|
|
||||||
request = rp.load()
|
|
||||||
except EOFError:
|
|
||||||
return 0
|
|
||||||
if self._verbose > 1: print("Got request: %s" % repr(request))
|
|
||||||
try:
|
|
||||||
methodname, args, id = request
|
|
||||||
if '.' in methodname:
|
|
||||||
reply = (None, self._special(methodname, args), id)
|
|
||||||
elif methodname[0] == '_':
|
|
||||||
raise NameError("illegal method name %s" % repr(methodname))
|
|
||||||
else:
|
|
||||||
method = getattr(self, methodname)
|
|
||||||
reply = (None, method(*args), id)
|
|
||||||
except:
|
|
||||||
reply = (sys.exc_info()[:2], id)
|
|
||||||
if id < 0 and reply[:2] == (None, None):
|
|
||||||
if self._verbose > 1: print("Suppress reply")
|
|
||||||
return 1
|
|
||||||
if self._verbose > 1: print("Send reply: %s" % repr(reply))
|
|
||||||
wp = pickle.Pickler(wf)
|
|
||||||
wp.dump(reply)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def _special(self, methodname, args):
|
|
||||||
if methodname == '.methods':
|
|
||||||
if not hasattr(self, '_methods'):
|
|
||||||
self._methods = tuple(self._listmethods())
|
|
||||||
return self._methods
|
|
||||||
raise NameError("unrecognized special method name %s" % repr(methodname))
|
|
||||||
|
|
||||||
def _listmethods(self, cl=None):
|
|
||||||
if not cl: cl = self.__class__
|
|
||||||
names = sorted([x for x in cl.__dict__.keys() if x[0] != '_'])
|
|
||||||
for base in cl.__bases__:
|
|
||||||
basenames = self._listmethods(base)
|
|
||||||
basenames = list(filter(lambda x, names=names: x not in names, basenames))
|
|
||||||
names[len(names):] = basenames
|
|
||||||
return names
|
|
||||||
|
|
||||||
|
|
||||||
from security import Security
|
|
||||||
|
|
||||||
|
|
||||||
class SecureServer(Server, Security):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
Server.__init__(self, *args)
|
|
||||||
Security.__init__(self)
|
|
||||||
|
|
||||||
def _verify(self, conn, address):
|
|
||||||
import string
|
|
||||||
challenge = self._generate_challenge()
|
|
||||||
conn.send("%d\n" % challenge)
|
|
||||||
response = ""
|
|
||||||
while "\n" not in response and len(response) < 100:
|
|
||||||
data = conn.recv(100)
|
|
||||||
if not data:
|
|
||||||
break
|
|
||||||
response = response + data
|
|
||||||
try:
|
|
||||||
response = string.atol(string.strip(response))
|
|
||||||
except string.atol_error:
|
|
||||||
if self._verbose > 0:
|
|
||||||
print("Invalid response syntax", repr(response))
|
|
||||||
return 0
|
|
||||||
if not self._compare_challenge_response(challenge, response):
|
|
||||||
if self._verbose > 0:
|
|
||||||
print("Invalid response value", repr(response))
|
|
||||||
return 0
|
|
||||||
if self._verbose > 1:
|
|
||||||
print("Response matches challenge. Go ahead!")
|
|
||||||
return 1
|
|
|
@ -1,27 +0,0 @@
|
||||||
import time
|
|
||||||
import sys
|
|
||||||
import FSProxy
|
|
||||||
|
|
||||||
def main():
|
|
||||||
t1 = time.time()
|
|
||||||
#proxy = FSProxy.FSProxyClient(('voorn.cwi.nl', 4127))
|
|
||||||
proxy = FSProxy.FSProxyLocal()
|
|
||||||
sumtree(proxy)
|
|
||||||
proxy._close()
|
|
||||||
t2 = time.time()
|
|
||||||
print(t2-t1, "seconds")
|
|
||||||
sys.stdout.write("[Return to exit] ")
|
|
||||||
sys.stdout.flush()
|
|
||||||
sys.stdin.readline()
|
|
||||||
|
|
||||||
def sumtree(proxy):
|
|
||||||
print("PWD =", proxy.pwd())
|
|
||||||
files = proxy.listfiles()
|
|
||||||
proxy.infolist(files)
|
|
||||||
subdirs = proxy.listsubdirs()
|
|
||||||
for name in subdirs:
|
|
||||||
proxy.cd(name)
|
|
||||||
sumtree(proxy)
|
|
||||||
proxy.back()
|
|
||||||
|
|
||||||
main()
|
|
Loading…
Reference in New Issue