summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Demo/scripts/README18
-rwxr-xr-xDemo/scripts/fact.py45
-rwxr-xr-xDemo/scripts/from.py25
-rwxr-xr-xDemo/scripts/lpwatch.py111
-rwxr-xr-xDemo/scripts/pi.py30
-rwxr-xr-xDemo/scripts/primes.py26
-rwxr-xr-xTools/scripts/byteyears.py29
-rwxr-xr-xTools/scripts/eptags.py50
-rwxr-xr-xTools/scripts/findlinksto.py29
-rwxr-xr-xTools/scripts/mkreal.py65
-rwxr-xr-xTools/scripts/objgraph.py213
-rwxr-xr-xTools/scripts/pdeps.py167
-rwxr-xr-xTools/scripts/ptags.py49
-rwxr-xr-xTools/scripts/suff.py29
-rwxr-xr-xTools/scripts/which.py44
-rwxr-xr-xTools/scripts/xxci.py77
16 files changed, 1007 insertions, 0 deletions
diff --git a/Demo/scripts/README b/Demo/scripts/README
new file mode 100644
index 0000000..7a2b837
--- /dev/null
+++ b/Demo/scripts/README
@@ -0,0 +1,18 @@
+Contents of this directory:
+
+byteyears.py Print product of a file's size and age
+eptags.py Create Emacs TAGS file for Python modules
+fact.py Factorize numbers
+findlinksto.py Find symbolic links to a given path (prefix)
+from.py Summarize mailbox
+lfact.py Factorize long numbers
+lpwatch.py Watch BSD line printer queues
+mkreal.py Turn a symbolic link into a real file or directory
+objgraph.py Print object graph from nm output on a library
+pdeps.py Print dependencies between Python modules
+pi.py Print digits of pi (uses arbitrary precision integers)
+primes.py Print prime numbers
+ptags.py Create vi tags file for Python modules
+suff.py Sort a list of files by suffix
+which.py Find a program in $PATH
+xxci.py Wrapper for rcsdiff and ci
diff --git a/Demo/scripts/fact.py b/Demo/scripts/fact.py
new file mode 100755
index 0000000..ba75a04
--- /dev/null
+++ b/Demo/scripts/fact.py
@@ -0,0 +1,45 @@
+#! /usr/local/python
+
+# Factorize numbers, slowly.
+# This version uses plain integers and is thus limited to 2**31-1.
+
+import sys
+from math import sqrt
+
+error = 'fact.error' # exception
+
+def fact(n):
+ if n < 1: raise error # fact() argument should be >= 1
+ if n = 1: return [] # special case
+ res = []
+ # Treat even factors special, so we can use i = i+2 later
+ while n%2 = 0:
+ res.append(2)
+ n = n/2
+ # Try odd numbers up to sqrt(n)
+ limit = int(sqrt(float(n+1)))
+ i = 3
+ while i <= limit:
+ if n%i = 0:
+ res.append(i)
+ n = n/i
+ limit = int(sqrt(float(n+1)))
+ else:
+ i = i+2
+ res.append(n)
+ return res
+
+def main():
+ if len(sys.argv) > 1:
+ for arg in sys.argv[1:]:
+ n = int(eval(arg))
+ print n, fact(n)
+ else:
+ try:
+ while 1:
+ n = int(input())
+ print n, fact(n)
+ except EOFError:
+ pass
+
+main()
diff --git a/Demo/scripts/from.py b/Demo/scripts/from.py
new file mode 100755
index 0000000..20771a0
--- /dev/null
+++ b/Demo/scripts/from.py
@@ -0,0 +1,25 @@
+#! /usr/local/python
+
+# Print From and Subject of messages in $MAIL.
+# Extension to multiple mailboxes and other bells & whistles are left
+# as exercises for the reader.
+
+import posix
+
+# Open mailbox file. Exits with exception when this fails.
+
+mail = open(posix.environ['MAIL'], 'r')
+
+while 1:
+ line = mail.readline()
+ if not line: break # EOF
+ if line[:5] = 'From ':
+ # Start of message found
+ print line[:-1],
+ while 1:
+ line = mail.readline()
+ if not line: break # EOF
+ if line = '\n': break # Blank line ends headers
+ if line[:8] = 'Subject:':
+ print `line[9:-1]`,
+ print
diff --git a/Demo/scripts/lpwatch.py b/Demo/scripts/lpwatch.py
new file mode 100755
index 0000000..294028f
--- /dev/null
+++ b/Demo/scripts/lpwatch.py
@@ -0,0 +1,111 @@
+#! /ufs/guido/bin/sgi/python
+#! /usr/local/python
+
+# Watch line printer queue(s).
+# Intended for BSD 4.3 lpq.
+
+import posix
+import sys
+import time
+import string
+
+DEF_PRINTER = 'psc'
+DEF_DELAY = 10
+
+def main():
+ delay = DEF_DELAY # XXX Use getopt() later
+ try:
+ thisuser = posix.environ['LOGNAME']
+ except:
+ thisuser = posix.environ['USER']
+ printers = sys.argv[1:]
+ if not printers:
+ if posix.environ.has_key('PRINTER'):
+ printers = [posix.environ['PRINTER']]
+ else:
+ printers = [DEF_PRINTER]
+ #
+ clearhome = posix.popen('clear', 'r').read()
+ #
+ while 1:
+ # Pipe output through cat for extra buffering,
+ # so the output (which overwrites the previous)
+ # appears instantaneous.
+ sys.stdout = posix.popen('exec cat', 'w')
+ sys.stdout.write(clearhome)
+ for name in printers:
+ pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r')
+ showstatus(name, pipe, thisuser)
+ sts = pipe.close()
+ if sts:
+ print name + ': *** lpq exit status', sts
+ sts = sys.stdout.close()
+ time.sleep(delay)
+
+def showstatus(name, pipe, thisuser):
+ lines = 0
+ users = {}
+ aheadbytes = 0
+ aheadjobs = 0
+ userseen = 0
+ totalbytes = 0
+ totaljobs = 0
+ while 1:
+ line = pipe.readline()
+ if not line: break
+ fields = string.split(line)
+ n = len(fields)
+ if len(fields) >= 6 and fields[n-1] = 'bytes':
+ rank = fields[0]
+ user = fields[1]
+ job = fields[2]
+ files = fields[3:-2]
+ bytes = eval(fields[n-2])
+ if user = thisuser:
+ userseen = 1
+ elif not userseen:
+ aheadbytes = aheadbytes + bytes
+ aheadjobs = aheadjobs + 1
+ totalbytes = totalbytes + bytes
+ totaljobs = totaljobs + 1
+ if users.has_key(user):
+ ujobs, ubytes = users[user]
+ else:
+ ujobs, ubytes = 0, 0
+ ujobs = ujobs + 1
+ ubytes = ubytes + bytes
+ users[user] = ujobs, ubytes
+ else:
+ if fields and fields[0] <> 'Rank':
+ if line[-1:] = '\n':
+ line = line[:-1]
+ if not lines:
+ print name + ':',
+ else:
+ print
+ print line,
+ lines = lines + 1
+ if totaljobs:
+ if lines > 1:
+ print
+ lines = lines+1
+ print (totalbytes+1023)/1024, 'K',
+ if totaljobs <> len(users):
+ print '(' + `totaljobs` + ' jobs)',
+ if len(users) = 1:
+ print 'for', users.keys()[0],
+ else:
+ print 'for', len(users), 'users',
+ if userseen:
+ if aheadjobs = 0:
+ print '(' + thisuser + ' first)',
+ else:
+ print '(' + `(aheadbytes+1023)/1024`,
+ print 'K before', thisuser + ')'
+ if lines:
+ print
+
+try:
+ main()
+except KeyboardInterrupt:
+ pass
diff --git a/Demo/scripts/pi.py b/Demo/scripts/pi.py
new file mode 100755
index 0000000..5e19db6
--- /dev/null
+++ b/Demo/scripts/pi.py
@@ -0,0 +1,30 @@
+#! /usr/local/python
+
+# Print digits of pi forever.
+#
+# The algorithm, using Python's 'long' integers ("bignums"), works
+# with continued fractions, and was conceived by Lambert Meertens.
+#
+# See also the ABC Programmer's Handbook, by Geurts, Meertens & Pemberton,
+# published by Prentice-Hall (UK) Ltd., 1990.
+
+import sys
+
+def main():
+ k, a, b, a1, b1 = 2l, 4l, 1l, 12l, 4l
+ while 1:
+ # Next approximation
+ p, q, k = k*k, 2l*k+1l, k+1l
+ a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
+ # Print common digits
+ d, d1 = a/b, a1/b1
+ #print a, b, a1, b1
+ while d = d1:
+ # Use write() to avoid spaces between the digits
+ sys.stdout.write(`int(d)`)
+ # Flush so the output is seen immediately
+ sys.stdout.flush()
+ a, a1 = 10l*(a%b), 10l*(a1%b1)
+ d, d1 = a/b, a1/b1
+
+main()
diff --git a/Demo/scripts/primes.py b/Demo/scripts/primes.py
new file mode 100755
index 0000000..487acef
--- /dev/null
+++ b/Demo/scripts/primes.py
@@ -0,0 +1,26 @@
+#! /usr/local/python
+
+# Print prime numbers in a given range
+
+def main():
+ import sys
+ min, max = 2, 0x7fffffff
+ if sys.argv[1:]:
+ min = int(eval(sys.argv[1]))
+ if sys.argv[2:]:
+ max = int(eval(sys.argv[2]))
+ primes(min, max)
+
+def primes(min, max):
+ if 2 >= min: print 2
+ primes = [2]
+ i = 3
+ while i <= max:
+ for p in primes:
+ if i%p = 0 or p*p > i: break
+ if i%p <> 0:
+ primes.append(i)
+ if i >= min: print i
+ i = i+2
+
+main()
diff --git a/Tools/scripts/byteyears.py b/Tools/scripts/byteyears.py
new file mode 100755
index 0000000..8c5ec69
--- /dev/null
+++ b/Tools/scripts/byteyears.py
@@ -0,0 +1,29 @@
+#! /usr/local/python
+
+# byteyears file ...
+#
+# Print a number representing the product of age and size of each file,
+# in suitable units.
+
+import sys, posix, time
+from stat import *
+
+secs_per_year = 365.0 * 24.0 * 3600.0
+now = time.time()
+status = 0
+
+for file in sys.argv[1:]:
+ try:
+ st = posix.stat(file)
+ except posix.error, msg:
+ sys.stderr.write('can\'t stat ' + `file` + ': ' + `msg` + '\n')
+ status = 1
+ st = ()
+ if st:
+ mtime = st[ST_MTIME]
+ size = st[ST_SIZE]
+ age = now - mtime
+ byteyears = float(size) * float(age) / secs_per_year
+ print file + '\t\t' + `int(byteyears)`
+
+sys.exit(status)
diff --git a/Tools/scripts/eptags.py b/Tools/scripts/eptags.py
new file mode 100755
index 0000000..1c682d3
--- /dev/null
+++ b/Tools/scripts/eptags.py
@@ -0,0 +1,50 @@
+#! /usr/local/python
+
+# eptags
+#
+# Create a TAGS file for Python programs, usable with GNU Emacs (version 18).
+# Tagged are:
+# - functions (even inside other defs or classes)
+# - classes
+# Warns about files it cannot open.
+# No warnings about duplicate tags.
+
+import sys
+import regexp
+
+def main():
+ outfp = open('TAGS', 'w')
+ args = sys.argv[1:]
+ for file in args:
+ treat_file(file, outfp)
+
+matcher = regexp.compile('^[ \t]*(def|class)[ \t]+([a-zA-Z0-9_]+)[ \t]*\(')
+
+def treat_file(file, outfp):
+ try:
+ fp = open(file, 'r')
+ except:
+ print 'Cannot open', file
+ return
+ charno = 0
+ lineno = 0
+ tags = []
+ size = 0
+ while 1:
+ line = fp.readline()
+ if not line: break
+ lineno = lineno + 1
+ res = matcher.exec(line)
+ if res:
+ (a, b), (a1, b1), (a2, b2) = res
+ name = line[a2:b2]
+ pat = line[a:b]
+ tag = pat + '\177' + `lineno` + ',' + `charno` + '\n'
+ tags.append(name, tag)
+ size = size + len(tag)
+ charno = charno + len(line)
+ outfp.write('\f\n' + file + ',' + `size` + '\n')
+ for name, tag in tags:
+ outfp.write(tag)
+
+main()
diff --git a/Tools/scripts/findlinksto.py b/Tools/scripts/findlinksto.py
new file mode 100755
index 0000000..6a2a75b
--- /dev/null
+++ b/Tools/scripts/findlinksto.py
@@ -0,0 +1,29 @@
+#! /usr/local/python
+
+# findlinksto
+#
+# find symbolic links to a given path
+
+import posix, path, sys
+
+def visit(pattern, dirname, names):
+ if path.islink(dirname):
+ names[:] = []
+ return
+ if path.ismount(dirname):
+ print 'descend into', dirname
+ n = len(pattern)
+ for name in names:
+ name = path.cat(dirname, name)
+ try:
+ linkto = posix.readlink(name)
+ if linkto[:n] = pattern:
+ print name, '->', linkto
+ except posix.error:
+ pass
+
+def main(pattern, args):
+ for dirname in args:
+ path.walk(dirname, visit, pattern)
+
+main(sys.argv[1], sys.argv[2:])
diff --git a/Tools/scripts/mkreal.py b/Tools/scripts/mkreal.py
new file mode 100755
index 0000000..3fd4b03
--- /dev/null
+++ b/Tools/scripts/mkreal.py
@@ -0,0 +1,65 @@
+#! /usr/local/python
+
+# mkreal
+#
+# turn a symlink to a directory into a real directory
+
+import sys
+import posix
+import path
+from stat import *
+
+cat = path.cat
+
+error = 'mkreal error'
+
+BUFSIZE = 32*1024
+
+def mkrealfile(name):
+ st = posix.stat(name) # Get the mode
+ mode = S_IMODE(st[ST_MODE])
+ linkto = posix.readlink(name) # Make sure again it's a symlink
+ f_in = open(name, 'r') # This ensures it's a file
+ posix.unlink(name)
+ f_out = open(name, 'w')
+ while 1:
+ buf = f_in.read(BUFSIZE)
+ if not buf: break
+ f_out.write(buf)
+ del f_out # Flush data to disk before changing mode
+ posix.chmod(name, mode)
+
+def mkrealdir(name):
+ st = posix.stat(name) # Get the mode
+ mode = S_IMODE(st[ST_MODE])
+ linkto = posix.readlink(name)
+ files = posix.listdir(name)
+ posix.unlink(name)
+ posix.mkdir(name, mode)
+ posix.chmod(name, mode)
+ linkto = cat('..', linkto)
+ #
+ for file in files:
+ if file not in ('.', '..'):
+ posix.symlink(cat(linkto, file), cat(name, file))
+
+def main():
+ sys.stdout = sys.stderr
+ progname = path.basename(sys.argv[0])
+ args = sys.argv[1:]
+ if not args:
+ print 'usage:', progname, 'path ...'
+ sys.exit(2)
+ status = 0
+ for name in args:
+ if not path.islink(name):
+ print progname+':', name+':', 'not a symlink'
+ status = 1
+ else:
+ if path.isdir(name):
+ mkrealdir(name)
+ else:
+ mkrealfile(name)
+ sys.exit(status)
+
+main()
diff --git a/Tools/scripts/objgraph.py b/Tools/scripts/objgraph.py
new file mode 100755
index 0000000..b45bba2
--- /dev/null
+++ b/Tools/scripts/objgraph.py
@@ -0,0 +1,213 @@
+#!/usr/local/python
+
+# objgraph
+#
+# Read "nm -o" input (on IRIX: "nm -Bo") of a set of libraries or modules
+# and print various interesting listings, such as:
+#
+# - which names are used but not defined in the set (and used where),
+# - which names are defined in the set (and where),
+# - which modules use which other modules,
+# - which modules are used by which other modules.
+#
+# Usage: objgraph [-cdu] [file] ...
+# -c: print callers per objectfile
+# -d: print callees per objectfile
+# -u: print usage of undefined symbols
+# If none of -cdu is specified, all are assumed.
+# Use "nm -o" to generate the input (on IRIX: "nm -Bo"),
+# e.g.: nm -o /lib/libc.a | objgraph
+
+
+import sys
+import string
+import path
+import getopt
+import regexp
+
+# Types of symbols.
+#
+definitions = 'TRGDSBAEC'
+externals = 'UV'
+ignore = 'Nntrgdsbavuc'
+
+# Regular expression to parse "nm -o" output.
+#
+matcher = regexp.compile('(.*):\t?........ (.) (.*)$')
+
+# Store "item" in "dict" under "key".
+# The dictionary maps keys to lists of items.
+# If there is no list for the key yet, it is created.
+#
+def store(dict, key, item):
+ if dict.has_key(key):
+ dict[key].append(item)
+ else:
+ dict[key] = [item]
+
+# Return a flattened version of a list of strings: the concatenation
+# of its elements with intervening spaces.
+#
+def flat(list):
+ s = ''
+ for item in list:
+ s = s + ' ' + item
+ return s[1:]
+
+# Global variables mapping defined/undefined names to files and back.
+#
+file2undef = {}
+def2file = {}
+file2def = {}
+undef2file = {}
+
+# Read one input file and merge the data into the tables.
+# Argument is an open file.
+#
+def readinput(file):
+ while 1:
+ s = file.readline(200) # Arbitrary, but reasonable limit
+ if not s:
+ break
+ # If you get an exception on this line,
+ # it is probably caused by an unexpected input line:
+ (ra, rb), (r1a, r1b), (r2a, r2b), (r3a, r3b) = matcher.exec(s)
+ fn, name, type = s[r1a:r1b], s[r3a:r3b], s[r2a:r2b]
+ if type in definitions:
+ store(def2file, name, fn)
+ store(file2def, fn, name)
+ elif type in externals:
+ store(file2undef, fn, name)
+ store(undef2file, name, fn)
+ elif not type in ignore:
+ print fn + ':' + name + ': unknown type ' + type
+
+# Print all names that were undefined in some module and where they are
+# defined.
+#
+def printcallee():
+ flist = file2undef.keys()
+ flist.sort()
+ for file in flist:
+ print file + ':'
+ elist = file2undef[file]
+ elist.sort()
+ for ext in elist:
+ if len(ext) >= 8:
+ tabs = '\t'
+ else:
+ tabs = '\t\t'
+ if not def2file.has_key(ext):
+ print '\t' + ext + tabs + ' *undefined'
+ else:
+ print '\t' + ext + tabs + flat(def2file[ext])
+
+# Print for each module the names of the other modules that use it.
+#
+def printcaller():
+ files = file2def.keys()
+ files.sort()
+ for file in files:
+ callers = []
+ for label in file2def[file]:
+ if undef2file.has_key(label):
+ callers = callers + undef2file[label]
+ if callers:
+ callers.sort()
+ print file + ':'
+ lastfn = ''
+ for fn in callers:
+ if fn <> lastfn:
+ print '\t' + fn
+ lastfn = fn
+ else:
+ print file + ': unused'
+
+# Print undefine names and where they are used.
+#
+def printundef():
+ undefs = {}
+ for file in file2undef.keys():
+ for ext in file2undef[file]:
+ if not def2file.has_key(ext):
+ store(undefs, ext, file)
+ elist = undefs.keys()
+ elist.sort()
+ for ext in elist:
+ print ext + ':'
+ flist = undefs[ext]
+ flist.sort()
+ for file in flist:
+ print '\t' + file
+
+# Print warning messages about names defined in more than one file.
+#
+def warndups():
+ savestdout = sys.stdout
+ sys.stdout = sys.stderr
+ names = def2file.keys()
+ names.sort()
+ for name in names:
+ if len(def2file[name]) > 1:
+ print 'warning:', name, 'multiply defined:',
+ print flat(def2file[name])
+ sys.stdout = savestdout
+
+# Main program
+#
+def main():
+ try:
+ optlist, args = getopt.getopt(sys.argv[1:], 'cdu')
+ except getopt.error:
+ sys.stdout = sys.stderr
+ print 'Usage:', path.basename(sys.argv[0]), '[-cdu] [file] ...'
+ print '-c: print callers per objectfile'
+ print '-d: print callees per objectfile'
+ print '-u: print usage of undefined symbols'
+ print 'If none of -cdu is specified, all are assumed.'
+ print 'Use "nm -o" to generate the input (on IRIX: "nm -Bo"),'
+ print 'e.g.: nm -o /lib/libc.a | objgraph'
+ return 1
+ optu = optc = optd = 0
+ for opt, void in optlist:
+ if opt = '-u':
+ optu = 1
+ elif opt = '-c':
+ optc = 1
+ elif opt = '-d':
+ optd = 1
+ if optu = optc = optd = 0:
+ optu = optc = optd = 1
+ if not args:
+ args = ['-']
+ for file in args:
+ if file = '-':
+ readinput(sys.stdin)
+ else:
+ readinput(open(file, 'r'))
+ #
+ warndups()
+ #
+ more = (optu + optc + optd > 1)
+ if optd:
+ if more:
+ print '---------------All callees------------------'
+ printcallee()
+ if optu:
+ if more:
+ print '---------------Undefined callees------------'
+ printundef()
+ if optc:
+ if more:
+ print '---------------All Callers------------------'
+ printcaller()
+ return 0
+
+# Call the main program.
+# Use its return value as exit status.
+# Catch interrupts to avoid stack trace.
+#
+try:
+ sys.exit(main())
+except KeyboardInterrupt:
+ sys.exit(1)
diff --git a/Tools/scripts/pdeps.py b/Tools/scripts/pdeps.py
new file mode 100755
index 0000000..2533015
--- /dev/null
+++ b/Tools/scripts/pdeps.py
@@ -0,0 +1,167 @@
+#! /usr/local/python
+
+# pdeps
+#
+# Find dependencies between a bunch of Python modules.
+#
+# Usage:
+# pdeps file1.py file2.py ...
+#
+# Output:
+# Four tables separated by lines like '--- Closure ---':
+# 1) Direct dependencies, listing which module imports which other modules
+# 2) The inverse of (1)
+# 3) Indirect dependencies, or the closure of the above
+# 4) The inverse of (3)
+#
+# To do:
+# - command line options to select output type
+# - option to automatically scan the Python library for referenced modules
+# - option to limit output to particular modules
+
+
+import sys
+import regexp
+import path
+import string
+
+
+# Main program
+#
+def main():
+ args = sys.argv[1:]
+ if not args:
+ print 'usage: pdeps file.py file.py ...'
+ return 2
+ #
+ table = {}
+ for arg in args:
+ process(arg, table)
+ #
+ print '--- Uses ---'
+ printresults(table)
+ #
+ print '--- Used By ---'
+ inv = inverse(table)
+ printresults(inv)
+ #
+ print '--- Closure of Uses ---'
+ reach = closure(table)
+ printresults(reach)
+ #
+ print '--- Closure of Used By ---'
+ invreach = inverse(reach)
+ printresults(invreach)
+ #
+ return 0
+
+
+# Compiled regular expressions to search for import statements
+#
+m_import = regexp.compile('^[ \t]*from[ \t]+([^ \t]+)[ \t]+')
+m_from = regexp.compile('^[ \t]*import[ \t]+([^#]+)')
+
+
+# Collect data from one file
+#
+def process(filename, table):
+ fp = open(filename, 'r')
+ mod = path.basename(filename)
+ if mod[-3:] = '.py':
+ mod = mod[:-3]
+ table[mod] = list = []
+ while 1:
+ line = fp.readline()
+ if not line: break
+ while line[-1:] = '\\':
+ nextline = fp.readline()
+ if not nextline: break
+ line = line[:-1] + nextline
+ result = m_import.exec(line)
+ if not result:
+ result = m_from.exec(line)
+ if result:
+ (a, b), (a1, b1) = result
+ words = string.splitfields(line[a1:b1], ',')
+ # print '#', line, words
+ for word in words:
+ word = string.strip(word)
+ if word not in list:
+ list.append(word)
+
+
+# Compute closure (this is in fact totally general)
+#
+def closure(table):
+ modules = table.keys()
+ #
+ # Initialize reach with a copy of table
+ #
+ reach = {}
+ for mod in modules:
+ reach[mod] = table[mod][:]
+ #
+ # Iterate until no more change
+ #
+ change = 1
+ while change:
+ change = 0
+ for mod in modules:
+ for mo in reach[mod]:
+ if mo in modules:
+ for m in reach[mo]:
+ if m not in reach[mod]:
+ reach[mod].append(m)
+ change = 1
+ #
+ return reach
+
+
+# Invert a table (this is again totally general).
+# All keys of the original table are made keys of the inverse,
+# so there may be empty lists in the inverse.
+#
+def inverse(table):
+ inv = {}
+ for key in table.keys():
+ if not inv.has_key(key):
+ inv[key] = []
+ for item in table[key]:
+ store(inv, item, key)
+ return inv
+
+
+# Store "item" in "dict" under "key".
+# The dictionary maps keys to lists of items.
+# If there is no list for the key yet, it is created.
+#
+def store(dict, key, item):
+ if dict.has_key(key):
+ dict[key].append(item)
+ else:
+ dict[key] = [item]
+
+
+# Tabulate results neatly
+#
+def printresults(table):
+ modules = table.keys()
+ maxlen = 0
+ for mod in modules: maxlen = max(maxlen, len(mod))
+ modules.sort()
+ for mod in modules:
+ list = table[mod]
+ list.sort()
+ print string.ljust(mod, maxlen), ':',
+ if mod in list:
+ print '(*)',
+ for ref in list:
+ print ref,
+ print
+
+
+# Call main and honor exit status
+try:
+ sys.exit(main())
+except KeyboardInterrupt:
+ sys.exit(1)
diff --git a/Tools/scripts/ptags.py b/Tools/scripts/ptags.py
new file mode 100755
index 0000000..b3a693e
--- /dev/null
+++ b/Tools/scripts/ptags.py
@@ -0,0 +1,49 @@
+#! /usr/local/python
+
+# ptags
+#
+# Create a tags file for Python programs, usable with vi.
+# Tagged are:
+# - functions (even inside other defs or classes)
+# - classes
+# - filenames
+# Warns about files it cannot open.
+# No warnings about duplicate tags.
+
+import sys
+import regexp
+import path
+
+tags = [] # Modified global variable!
+
+def main():
+ args = sys.argv[1:]
+ for file in args: treat_file(file)
+ if tags:
+ fp = open('tags', 'w')
+ tags.sort()
+ for s in tags: fp.write(s)
+
+matcher = regexp.compile('^[ \t]*(def|class)[ \t]+([a-zA-Z0-9_]+)[ \t]*\(')
+
+def treat_file(file):
+ try:
+ fp = open(file, 'r')
+ except:
+ print 'Cannot open', file
+ return
+ base = path.basename(file)
+ if base[-3:] = '.py': base = base[:-3]
+ s = base + '\t' + file + '\t' + '1\n'
+ tags.append(s)
+ while 1:
+ line = fp.readline()
+ if not line: break
+ res = matcher.exec(line)
+ if res:
+ (a, b), (a1, b1), (a2, b2) = res
+ name = line[a2:b2]
+ s = name + '\t' + file + '\t/^' + line[a:b] + '/\n'
+ tags.append(s)
+
+main()
diff --git a/Tools/scripts/suff.py b/Tools/scripts/suff.py
new file mode 100755
index 0000000..7fda113
--- /dev/null
+++ b/Tools/scripts/suff.py
@@ -0,0 +1,29 @@
+#! /usr/local/python
+
+# suff
+#
+# show different suffixes amongst arguments
+
+import sys
+
+def main():
+ files = sys.argv[1:]
+ suffixes = {}
+ for file in files:
+ suff = getsuffix(file)
+ if not suffixes.has_key(suff):
+ suffixes[suff] = []
+ suffixes[suff].append(file)
+ keys = suffixes.keys()
+ keys.sort()
+ for suff in keys:
+ print `suff`, len(suffixes[suff])
+
+def getsuffix(file):
+ suff = ''
+ for i in range(len(file)):
+ if file[i] = '.':
+ suff = file[i:]
+ return suff
+
+main()
diff --git a/Tools/scripts/which.py b/Tools/scripts/which.py
new file mode 100755
index 0000000..b9b888b
--- /dev/null
+++ b/Tools/scripts/which.py
@@ -0,0 +1,44 @@
+#! /usr/local/python
+
+# Variant of "which".
+# On stderr, near and total misses are reported.
+
+import sys, posix, string, path
+from stat import *
+
+def msg(str):
+ sys.stderr.write(str + '\n')
+
+pathlist = string.splitfields(posix.environ['PATH'], ':')
+
+sts = 0
+
+for prog in sys.argv[1:]:
+ ident = ()
+ for dir in pathlist:
+ file = path.cat(dir, prog)
+ try:
+ st = posix.stat(file)
+ if S_ISREG(st[ST_MODE]):
+ mode = S_IMODE(st[ST_MODE])
+ if mode % 2 or mode/8 % 2 or mode/64 % 2:
+ if ident:
+ if st[:3] = ident:
+ s = ': same as '
+ else:
+ s = ': also '
+ msg(prog + s + file)
+ else:
+ print file
+ ident = st[:3]
+ else:
+ msg(file + ': not executable')
+ else:
+ msg(file + ': not a disk file')
+ except posix.error:
+ pass
+ if not ident:
+ msg(prog + ': not found')
+ sts = 1
+
+sys.exit(sts)
diff --git a/Tools/scripts/xxci.py b/Tools/scripts/xxci.py
new file mode 100755
index 0000000..e747c8d
--- /dev/null
+++ b/Tools/scripts/xxci.py
@@ -0,0 +1,77 @@
+#! /usr/local/python
+
+# xxci
+#
+# check in files for which rcsdiff returns nonzero exit status
+
+import sys
+import posix
+import stat
+import path
+import commands
+
+MAXSIZE = 200*1024 # Files this big must be binaries and are skipped.
+
+def getargs():
+ args = sys.argv[1:]
+ if args:
+ return args
+ print 'No arguments, checking almost *'
+ for file in posix.listdir('.'):
+ if not skipfile(file):
+ args.append(file)
+ if not args:
+ print 'Nothing to do -- exit 1'
+ sys.exit(1)
+ args.sort()
+ return args
+
+badnames = ['tags', 'xyzzy']
+badprefixes = ['.', ',', '@', '#', 'o.']
+badsuffixes = \
+ ['~', '.a', '.o', '.old', '.bak', '.orig', '.new', '.prev', '.not']
+# XXX Should generalize even more to use fnmatch!
+
+def skipfile(file):
+ if file in badnames or \
+ badprefix(file) or badsuffix(file) or \
+ path.islink(file) or path.isdir(file):
+ return 1
+ # Skip huge files -- probably binaries.
+ try:
+ st = posix.stat(file)
+ except posix.error:
+ return 1 # Doesn't exist -- skip it
+ return st[stat.ST_SIZE] >= MAXSIZE
+
+def badprefix(file):
+ for bad in badprefixes:
+ if file[:len(bad)] = bad: return 1
+ return 0
+
+def badsuffix(file):
+ for bad in badsuffixes:
+ if file[-len(bad):] = bad: return 1
+ return 0
+
+def go(args):
+ for file in args:
+ print file + ':'
+ if run('rcsdiff -c', file):
+ if askyesno('Check in ' + file + ' ? '):
+ sts = run('rcs -l', file) # ignored
+ # can't use run() here because it's interactive
+ sts = posix.system('ci -l ' + file)
+
+def run(cmd, file):
+ sts, output = commands.getstatusoutput(cmd + commands.mkarg(file))
+ if sts:
+ print output
+ print 'Exit status', sts
+ return sts
+
+def askyesno(prompt):
+ s = raw_input(prompt)
+ return s in ['y', 'yes']
+
+go(getargs())