summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xDemo/pdist/RCSProxy.py285
-rw-r--r--Demo/pdist/README2
-rwxr-xr-xDemo/pdist/cvslib.py186
-rwxr-xr-xDemo/pdist/rcvs.py192
-rwxr-xr-xDemo/pdist/rrcs.py174
5 files changed, 839 insertions, 0 deletions
diff --git a/Demo/pdist/RCSProxy.py b/Demo/pdist/RCSProxy.py
new file mode 100755
index 0000000..0a12157
--- /dev/null
+++ b/Demo/pdist/RCSProxy.py
@@ -0,0 +1,285 @@
+#! /usr/local/bin/python
+
+"""RCS Proxy.
+
+Provide a simplified interface on RCS files, locally or remotely.
+The functionality is geared towards implementing some sort of
+remote CVS like utility. It is modeled after the similar module
+FSProxy.
+
+The module defines three classes:
+
+RCSProxyLocal -- used for local access
+RCSProxyServer -- used on the server side of remote access
+RCSProxyClient -- used on the client side of remote access
+
+The remote classes are instantiated with an IP address and an optional
+verbosity flag.
+"""
+
+import server
+import client
+import md5
+import os
+import fnmatch
+import string
+import tempfile
+
+
+okchars = string.letters + string.digits + '-_=+.'
+
+
+class RCSProxyLocal:
+
+ def __init__(self):
+ self._dirstack = []
+
+ def _close(self):
+ while self._dirstack:
+ self.back()
+
+ def pwd(self):
+ return os.getcwd()
+
+ def cd(self, name):
+ save = os.getcwd()
+ os.chdir(name)
+ self._dirstack.append(save)
+
+ def back(self):
+ if not self._dirstack:
+ raise os.error, "empty directory stack"
+ dir = self._dirstack[-1]
+ os.chdir(dir)
+ del self._dirstack[-1]
+
+ def _filter(self, files, pat = None):
+ if pat:
+ def keep(name, pat = pat):
+ return fnmatch.fnmatch(name, pat)
+ files = filter(keep, files)
+ files.sort()
+ return files
+
+ def isfile(self, name):
+ namev = name + ',v'
+ return os.path.isfile(namev) or \
+ os.path.isfile(os.path.join('RCS', namev))
+
+ def _unmangle(self, name):
+ if type(name) == type(''):
+ rev = ''
+ else:
+ name, rev = name
+ return name, rev
+
+ def checkfile(self, name):
+ name, rev = self._unmangle(name)
+ if not self.isfile(name):
+ raise os.error, 'not an rcs file %s' % `name`
+ for c in rev:
+ if c not in okchars:
+ raise ValueError, "bad char in rev"
+ return name, rev
+
+ def listfiles(self, pat = None):
+ def isrcs(name): return name[-2:] == ',v'
+ def striprcs(name): return name[:-2]
+ files = os.listdir(os.curdir)
+ files = filter(isrcs, files)
+ if os.path.isdir('RCS'):
+ files2 = os.listdir('RCS')
+ files2 = filter(isrcs, files2)
+ files = files + files2
+ files = map(striprcs, files)
+ return self._filter(files, pat)
+
+ def listsubdirs(self, pat = None):
+ files = os.listdir(os.curdir)
+ files = filter(os.path.isdir, files)
+ return self._filter(files, pat)
+
+ def isdir(self, name):
+ return os.path.isdir(name)
+
+ def _open(self, name, cmd = 'co -p'):
+ name, rev = self.checkfile(name)
+ namev = name + ',v'
+ if rev:
+ cmd = cmd + ' -r' + rev
+ return os.popen('%s %s' % (cmd, `namev`))
+
+ def _closepipe(self, f):
+ sts = f.close()
+ if sts:
+ raise IOError, "Exit status %d" % sts
+
+ def _remove(self, fn):
+ try:
+ os.unlink(fn)
+ except os.error:
+ pass
+
+ def sum(self, name):
+ f = self._open(name)
+ BUFFERSIZE = 1024*8
+ sum = md5.new()
+ while 1:
+ buffer = f.read(BUFFERSIZE)
+ if not buffer:
+ break
+ sum.update(buffer)
+ self._closepipe(f)
+ return sum.digest()
+
+ def _list(self, function, list):
+ if list is None:
+ list = self.listfiles()
+ res = []
+ for name in list:
+ try:
+ res.append((name, function(name)))
+ except (os.error, IOError):
+ res.append((name, None))
+ return res
+
+ def sumlist(self, list = None):
+ return self.list(self.sum, list)
+
+ def _dict(self, function, list):
+ if list is None:
+ list = self.listfiles()
+ dict = {}
+ for name in list:
+ try:
+ dict[name] = function(name)
+ except (os.error, IOError):
+ pass
+ return dict
+
+ def sumdict(self, list = None):
+ return self.dict(self.sum, list)
+
+ def get(self, name):
+ f = self._open(name)
+ data = f.read()
+ self._closepipe(f)
+ return data
+
+ def info(self, name):
+ f = self._open(name, 'rlog -h')
+ dict = {}
+ while 1:
+ line = f.readline()
+ if not line: break
+ if line[0] == '\t':
+ continue # XXX lock details, later
+ i = string.find(line, ':')
+ if i > 0:
+ key, value = line[:i], string.strip(line[i+1:])
+ dict[key] = value
+ self._closepipe(f)
+ return dict
+
+ def head(self, name):
+ dict = self.info(name)
+ return dict['head']
+
+ def log(self, name, flags = ''):
+ f = self._open(name, 'rlog %s 2>&1' % flags)
+ log = f.read()
+ self._closepipe(f)
+ return log
+
+ def put(self, fullname, data, message = ""):
+ if message and message[-1] != '\n':
+ message = message + '\n'
+ name, rev = self._unmangle(fullname)
+ new = not self.isfile(name)
+ if new:
+ for c in name:
+ if c not in okchars:
+ raise ValueError, "bad char in name"
+ else:
+ self._remove(name)
+ f = open(name, 'w')
+ f.write(data)
+ f.close()
+ tf = tempfile.mktemp()
+ try:
+ if not new:
+ cmd = "rcs -l%s %s >>%s 2>&1" % (rev, name, tf)
+ sts = os.system(cmd)
+ if sts:
+ raise IOError, "rcs -l exit status %d" % sts
+ cmd = "ci -r%s %s >>%s 2>&1" % (rev, name, tf)
+ p = os.popen(cmd, 'w')
+ p.write(message)
+ sts = p.close()
+ if sts:
+ raise IOError, "ci exit status %d" % sts
+ messages = open(tf).read()
+ return messages or None
+ finally:
+ self._remove(tf)
+
+ def mkdir(self, name):
+ os.mkdir(name, 0777)
+
+ def rmdir(self, name):
+ os.rmdir(name)
+
+
+class RCSProxyServer(RCSProxyLocal, server.Server):
+
+ def __init__(self, address, verbose = server.VERBOSE):
+ RCSProxyLocal.__init__(self)
+ server.Server.__init__(self, address, verbose)
+
+ def _close(self):
+ server.Server._close(self)
+ RCSProxyLocal._close(self)
+
+ def _serve(self):
+ server.Server._serve(self)
+ # Retreat into start directory
+ while self._dirstack: self.back()
+
+
+class RCSProxyClient(client.Client):
+
+ def __init__(self, address, verbose = client.VERBOSE):
+ client.Client.__init__(self, address, verbose)
+
+
+def test_server():
+ import string
+ import sys
+ if sys.argv[1:]:
+ port = string.atoi(sys.argv[1])
+ else:
+ port = 4127
+ proxy = RCSProxyServer(('', port))
+ proxy._serverloop()
+
+
+def test():
+ import sys
+ if not sys.argv[1:] or sys.argv[1] and sys.argv[1][0] in '0123456789':
+ test_server()
+ sys.exit(0)
+ proxy = RCSProxyLocal()
+ what = sys.argv[1]
+ if hasattr(proxy, what):
+ attr = getattr(proxy, what)
+ if callable(attr):
+ print apply(attr, tuple(sys.argv[2:]))
+ else:
+ print `attr`
+ else:
+ print "%s: no such attribute" % what
+ sys.exit(2)
+
+
+if __name__ == '__main__':
+ test()
diff --git a/Demo/pdist/README b/Demo/pdist/README
new file mode 100644
index 0000000..738126d
--- /dev/null
+++ b/Demo/pdist/README
@@ -0,0 +1,2 @@
+This directory contains various modules and classes that support
+remote file system operations
diff --git a/Demo/pdist/cvslib.py b/Demo/pdist/cvslib.py
new file mode 100755
index 0000000..c98f0ba
--- /dev/null
+++ b/Demo/pdist/cvslib.py
@@ -0,0 +1,186 @@
+"""Utilities to read and write CVS admin files (esp. CVS/Entries)"""
+
+import string
+import os
+import time
+
+
+class Entry:
+
+ """Class representing one (parsed) line from CVS/Entries"""
+
+ def __init__(self, line):
+ words = string.splitfields(line, '/')
+ self.file = words[1]
+ self.rev = words[2]
+ dates = words[3] # ctime, mtime
+ if dates[:7] == 'Initial':
+ self.ctime = None
+ self.mtime = None
+ self.new = 1
+ else:
+ self.ctime = unctime(dates[:24])
+ self.mtime = unctime(dates[25:])
+ self.new = 0
+ self.extra = words[4]
+ self.sum = None
+
+ def unparse(self):
+ if self.new:
+ dates = "Initial %s" % self.file
+ else:
+ dates = gmctime(self.ctime) + ' ' + gmctime(self.mtime)
+ return "/%s/%s/%s/%s/\n" % (
+ self.file,
+ self.rev,
+ dates,
+ self.extra)
+
+ def setsum(self, sum):
+ self.sum = sum
+
+ def getsum(self):
+ return self.sum
+
+ def sethexsum(self, hexsum):
+ self.setsum(unhexify(hexsum))
+
+ def gethexsum(self):
+ if self.sum:
+ return hexify(self.sum)
+ else:
+ return None
+
+
+class CVS:
+
+ """Class representing the contents of CVS/Entries (and CVS/Sums)"""
+
+ def __init__(self):
+ self.readentries()
+
+ def readentries(self):
+ self.entries = {}
+ f = self.cvsopen("Entries")
+ while 1:
+ line = f.readline()
+ if not line: break
+ e = Entry(line)
+ self.entries[e.file] = e
+ f.close()
+
+ def readsums(self):
+ try:
+ f = self.cvsopen("Sums")
+ except IOError:
+ return
+ while 1:
+ line = f.readline()
+ if not line: break
+ words = string.split(line)
+ [file, rev, hexsum] = words
+ e = self.entries[file]
+ if e.rev == rev:
+ e.sethexsum(hexsum)
+ f.close()
+
+ def writeentries(self):
+ f = self.cvsopen("Entries", 'w')
+ for file in self.keys():
+ f.write(self.entries[file].unparse())
+ f.close()
+
+ def writesums(self):
+ if self.cvsexists("Sums"):
+ f = self.cvsopen("Sums", 'w')
+ else:
+ f = None
+ for file in self.keys():
+ e = self.entries[file]
+ hexsum = e.gethexsum()
+ if hexsum:
+ if not f:
+ f = self.cvsopen("Sums", 'w')
+ f.write("%s %s %s\n" % (file, e.rev, hexsum))
+ if f:
+ f.close()
+
+ def keys(self):
+ keys = self.entries.keys()
+ keys.sort()
+ return 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 + '~'
+ os.rename(file, bfile)
+
+
+hexify_format = '%02x' * 16
+def hexify(sum):
+ "Return a hex representation of a 16-byte string (e.g. an MD5 digest)"
+ return hexify_format % tuple(map(ord, sum))
+
+def unhexify(hexsum):
+ "Return the original from a hexified string"
+ 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 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] = 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):
+ 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()
+ keys = x.entries.keys()
+ keys.sort()
+ for file in keys:
+ e = x.entries[file]
+ print file, e.rev, gmctime(e.ctime), gmctime(e.mtime), e.extra,
+ print e.gethexsum()
+
+
+if __name__ == "__main__":
+ test()
diff --git a/Demo/pdist/rcvs.py b/Demo/pdist/rcvs.py
new file mode 100755
index 0000000..eb43bb8
--- /dev/null
+++ b/Demo/pdist/rcvs.py
@@ -0,0 +1,192 @@
+from cvslib import CVS, Entry
+import RCSProxy
+import client
+import md5
+import os
+import string
+import sys
+import time
+import fnmatch
+
+
+ignored_patterns = ['*.pyc', '.*', '*~', '@*']
+def ignored(file):
+ if os.path.isdir(file): return 1
+ for pat in ignored_patterns:
+ if fnmatch.fnmatch(file, pat): return 1
+ return 0
+
+
+class PCVS(CVS):
+
+ def __init__(self, proxy):
+ CVS.__init__(self)
+ self.proxy = proxy
+ self.readsums()
+ self.calcsums()
+
+ def calcsums(self):
+ for file in self.keys():
+ e = self.entries[file]
+ if not e.new and e.sum is None:
+ sum = self.proxy.sum((file, e.rev))
+ e.setsum(sum)
+
+ def fullcheck(self):
+ ok = 1
+ for file in self.keys():
+ e = self.entries[file]
+ if e.new:
+ if self.proxy.isfile(file):
+ print "%s: created by someone else!"
+ ok = 0
+ continue
+ rrev = self.proxy.head(file)
+ if rrev != e.rev:
+ print "%s: out of date (%s vs. %s)" % \
+ (file, e.rev, rrev)
+ ok = 0
+ return ok
+
+ def update(self):
+ for file in self.keys():
+ e = self.entries[file]
+ if e.new:
+ print 'A', file
+ continue
+ rrev = self.proxy.head(file)
+ lsum = sumfile(file)
+ if rrev == e.rev:
+ if lsum == e.sum:
+ print '=', file
+ else:
+ print 'M', file
+ continue
+ if e.sum != lsum:
+ print "%s: conflict -- not updated" % file
+ continue
+ print "%s: getting ..." % file
+ data = self.proxy.get(file)
+ f = open(file, 'w')
+ f.write(data)
+ f.close()
+ nsum = md5.new(data).digest()
+ e.setsum(nsum)
+ e.rev = rrev
+ print 'U', file
+ self.writeentries()
+ self.writesums()
+
+ def commit(self):
+ if not self.fullcheck():
+ print "correct above errors first"
+ return
+ needed = []
+ for file in self.keys():
+ e = self.entries[file]
+ if e.new:
+ needed.append(file)
+ continue
+ lsum = sumfile(file)
+ if lsum != e.sum:
+ needed.append(file)
+ continue
+ if not needed:
+ print "no changes need committing"
+ return
+ message = raw_input("One-liner: ")
+ for file in needed:
+ print "%s: putting ..." % file
+ e = self.entries[file]
+ data = open(file).read()
+ self.proxy.put(file, data, message)
+ e.rev = self.proxy.head(file)
+ e.setsum(self.proxy.sum(file))
+ # XXX get it?
+ mtime, ctime = os.stat(file)[-2:]
+ e.mtime = mtime
+ e.ctime = ctime
+ self.writeentries()
+ self.writesums()
+
+ def report(self):
+ keys = self.keys()
+ files = os.listdir(os.curdir)
+ allfiles = files
+ for file in keys:
+ if file not in allfiles:
+ allfiles.append(file)
+ allfiles.sort()
+ for file in allfiles:
+ if file not in keys:
+ if not ignored(file):
+ print '?', file
+ continue
+ if file not in files:
+ print file, ': lost'
+ continue
+ e = self.entries[file]
+ if not os.path.exists(file):
+ print "%s: lost" % file
+ continue
+ if e.new:
+ print 'A', file
+ continue
+ lsum = sumfile(file)
+ rrev = self.proxy.head(file)
+ if rrev == e.rev:
+ if lsum == e.sum:
+ print '=', file
+ else:
+ print 'M', file
+ else:
+ if lsum == e.sum:
+ print 'U', file
+ else:
+ print 'C', file
+
+ def add(self, file):
+ if self.entries.has_key(file):
+ print "%s: already known"
+ else:
+ self.entries[file] = Entry('/%s/0/Initial %s//\n' %
+ (file, file))
+
+
+def sumfile(file):
+ return md5.new(open(file).read()).digest()
+
+
+def test():
+ proxy = RCSProxy.RCSProxyClient(('voorn.cwi.nl', 4127))
+ proxy.cd('/ufs/guido/voorn/python-RCS/Demo/pdist')
+ x = PCVS(proxy)
+ args = sys.argv[1:]
+ if args:
+ cmd = args[0]
+ files = args[1:]
+ if cmd == 'add':
+ if not files:
+ print "add needs at least one file argument"
+ else:
+ for file in files:
+ x.add(file)
+ x.writeentries()
+ elif cmd in ('update', 'up'):
+ if files:
+ print "updates wants no file arguments"
+ else:
+ x.update()
+ elif cmd in ('commit', 'com'):
+ if files:
+ print "commit wants no file arguments"
+ else:
+ x.commit()
+ else:
+ print "Unknown command", cmd
+ else:
+ x.report()
+ if sys.argv[1:]: x.writesums()
+
+if __name__ == "__main__":
+ test()
diff --git a/Demo/pdist/rrcs.py b/Demo/pdist/rrcs.py
new file mode 100755
index 0000000..74bce56
--- /dev/null
+++ b/Demo/pdist/rrcs.py
@@ -0,0 +1,174 @@
+#! /usr/local/bin/python
+
+import sys
+import os
+import getopt
+import string
+import md5
+import tempfile
+
+def main():
+ sys.stdout = sys.stderr
+ try:
+ opts, rest = getopt.getopt(sys.argv[1:], 'h:p:qv')
+ if not rest:
+ raise getopt.error, "missing command"
+ cmd, rest = rest[0], rest[1:]
+ if not commands.has_key(cmd):
+ raise getopt.error, "unknown command"
+ coptset, func = commands[cmd]
+ copts, files = getopt.getopt(rest, coptset)
+ except getopt.error, 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 = openclient(opts)
+ if not files:
+ files = x.listfiles()
+ for fn in files:
+ try:
+ func(x, copts, fn)
+ except (IOError, os.error), msg:
+ print "%s: %s" % (fn, msg)
+
+def openclient(opts):
+ import client
+ import RCSProxy
+ host = 'spam'
+ port = 4127
+ verbose = client.VERBOSE
+ 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 == '-v':
+ verbose = verbose + 1
+ if o == '-q':
+ verbose = 0
+ address = (host, port)
+ x = RCSProxy.RCSProxyClient(address, verbose)
+ return x
+
+def checkin(x, copts, fn):
+ f = open(fn)
+ data = f.read()
+ f.close()
+ new = not x.isfile(fn)
+ if not new and same(x, copts, fn, data):
+ print "%s: unchanged since last checkin" % fn
+ return
+ 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 info(x, copts, fn):
+ dict = x.info(fn)
+ keys = dict.keys()
+ keys.sort()
+ for key in keys:
+ print key + ':', dict[key]
+ print '='*70
+
+def head(x, copts, fn):
+ head = x.head(fn)
+ print fn, head
+
+def list(x, copts, fn):
+ if x.isfile(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)
+ tfn = tempfile.mktemp()
+ try:
+ tf = open(tfn, 'w')
+ tf.write(data)
+ tf.close()
+ print 'diff %s -r%s %s' % (flags, x.head(fn), fn)
+ sts = os.system('diff %s %s %s' % (flags, tfn, fn))
+ if sts:
+ print '='*70
+ finally:
+ remove(tfn)
+
+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,",
+ else:
+ print "enter log message,",
+ 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),
+ 'log': ('bhLRtd:l:r:s:w:V:', log),
+ 'diff': ('c', diff),
+ }
+
+main()