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.
|
||||
|
||||
pdist Old, unfinished code messing with CVS, RCS and remote
|
||||
files.
|
||||
|
||||
pysvr An example of embedding Python in a threaded
|
||||
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