summaryrefslogtreecommitdiff
path: root/sys/src/cmd/python/Demo/scripts
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@localhost>2011-05-03 11:25:13 +0000
committercinap_lenrek <cinap_lenrek@localhost>2011-05-03 11:25:13 +0000
commit458120dd40db6b4df55a4e96b650e16798ef06a0 (patch)
tree8f82685be24fef97e715c6f5ca4c68d34d5074ee /sys/src/cmd/python/Demo/scripts
parent3a742c699f6806c1145aea5149bf15de15a0afd7 (diff)
add hg and python
Diffstat (limited to 'sys/src/cmd/python/Demo/scripts')
-rw-r--r--sys/src/cmd/python/Demo/scripts/README23
-rw-r--r--sys/src/cmd/python/Demo/scripts/beer.py14
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/eqfix.py198
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/fact.py49
-rw-r--r--sys/src/cmd/python/Demo/scripts/find-uname.py40
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/from.py35
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/ftpstats.py145
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/lpwatch.py110
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/makedir.py21
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/markov.py117
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/mboxconvert.py124
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/mkrcs.py61
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/morse.py149
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/newslist.doc59
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/newslist.py366
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/pi.py34
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/pp.py130
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/primes.py27
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/queens.py85
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/script.py33
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/unbirthday.py107
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/update.py92
-rwxr-xr-xsys/src/cmd/python/Demo/scripts/wh.py2
23 files changed, 2021 insertions, 0 deletions
diff --git a/sys/src/cmd/python/Demo/scripts/README b/sys/src/cmd/python/Demo/scripts/README
new file mode 100644
index 000000000..d8434e8d4
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/README
@@ -0,0 +1,23 @@
+This directory contains a collection of executable Python scripts.
+
+See also the Tools/scripts directory!
+
+beer.py Print the classic 'bottles of beer' list.
+eqfix.py Fix .py files to use the correct equality test operator
+fact.py Factorize numbers
+find-uname.py Search for Unicode characters using regexps.
+from.py Summarize mailbox
+ftpstats.py Summarize ftp daemon log file
+lpwatch.py Watch BSD line printer queues
+makedir.py Like mkdir -p
+markov.py Markov chain simulation of words or characters
+mboxconvvert.py Convert MH or MMDF mailboxes to unix mailbox format
+mkrcs.py Fix symlinks named RCS into parallel tree
+morse.py Produce morse code (audible or on AIFF file)
+pi.py Print all digits of pi -- given enough time and memory
+pp.py Emulate some Perl command line options
+primes.py Print prime numbers
+queens.py Dijkstra's solution to Wirth's "N Queens problem"
+script.py Equivalent to BSD script(1) -- by Steen Lumholt
+unbirthday.py Print unbirthday count
+update.py Update a bunch of files according to a script.
diff --git a/sys/src/cmd/python/Demo/scripts/beer.py b/sys/src/cmd/python/Demo/scripts/beer.py
new file mode 100644
index 000000000..1b9ac8bad
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/beer.py
@@ -0,0 +1,14 @@
+#! /usr/bin/env python
+# By GvR, demystified after a version by Fredrik Lundh.
+import sys
+n = 100
+if sys.argv[1:]: n = int(sys.argv[1])
+def bottle(n):
+ if n == 0: return "no more bottles of beer"
+ if n == 1: return "one bottle of beer"
+ return str(n) + " bottles of beer"
+for i in range(n):
+ print bottle(n-i), "on the wall,"
+ print bottle(n-i) + "."
+ print "Take one down, pass it around,"
+ print bottle(n-i-1), "on the wall."
diff --git a/sys/src/cmd/python/Demo/scripts/eqfix.py b/sys/src/cmd/python/Demo/scripts/eqfix.py
new file mode 100755
index 000000000..35c43aa04
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/eqfix.py
@@ -0,0 +1,198 @@
+#! /usr/bin/env python
+
+# Fix Python source files to use the new equality test operator, i.e.,
+# if x = y: ...
+# is changed to
+# if x == y: ...
+# The script correctly tokenizes the Python program to reliably
+# distinguish between assignments and equality tests.
+#
+# Command line arguments are files or directories to be processed.
+# Directories are searched recursively for files whose name looks
+# like a python module.
+# Symbolic links are always ignored (except as explicit directory
+# arguments). Of course, the original file is kept as a back-up
+# (with a "~" attached to its name).
+# It complains about binaries (files containing null bytes)
+# and about files that are ostensibly not Python files: if the first
+# line starts with '#!' and does not contain the string 'python'.
+#
+# Changes made are reported to stdout in a diff-like format.
+#
+# Undoubtedly you can do this using find and sed or perl, but this is
+# a nice example of Python code that recurses down a directory tree
+# and uses regular expressions. Also note several subtleties like
+# preserving the file's mode and avoiding to even write a temp file
+# when no changes are needed for a file.
+#
+# NB: by changing only the function fixline() you can turn this
+# into a program for a different change to Python programs...
+
+import sys
+import re
+import os
+from stat import *
+import string
+
+err = sys.stderr.write
+dbg = err
+rep = sys.stdout.write
+
+def main():
+ bad = 0
+ if not sys.argv[1:]: # No arguments
+ err('usage: ' + sys.argv[0] + ' file-or-directory ...\n')
+ sys.exit(2)
+ for arg in sys.argv[1:]:
+ if os.path.isdir(arg):
+ if recursedown(arg): bad = 1
+ elif os.path.islink(arg):
+ err(arg + ': will not process symbolic links\n')
+ bad = 1
+ else:
+ if fix(arg): bad = 1
+ sys.exit(bad)
+
+ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$')
+def ispython(name):
+ return ispythonprog.match(name) >= 0
+
+def recursedown(dirname):
+ dbg('recursedown(%r)\n' % (dirname,))
+ bad = 0
+ try:
+ names = os.listdir(dirname)
+ except os.error, msg:
+ err('%s: cannot list directory: %r\n' % (dirname, msg))
+ return 1
+ names.sort()
+ subdirs = []
+ for name in names:
+ if name in (os.curdir, os.pardir): continue
+ fullname = os.path.join(dirname, name)
+ if os.path.islink(fullname): pass
+ elif os.path.isdir(fullname):
+ subdirs.append(fullname)
+ elif ispython(name):
+ if fix(fullname): bad = 1
+ for fullname in subdirs:
+ if recursedown(fullname): bad = 1
+ return bad
+
+def fix(filename):
+## dbg('fix(%r)\n' % (dirname,))
+ try:
+ f = open(filename, 'r')
+ except IOError, msg:
+ err('%s: cannot open: %r\n' % (filename, msg))
+ return 1
+ head, tail = os.path.split(filename)
+ tempname = os.path.join(head, '@' + tail)
+ g = None
+ # If we find a match, we rewind the file and start over but
+ # now copy everything to a temp file.
+ lineno = 0
+ while 1:
+ line = f.readline()
+ if not line: break
+ lineno = lineno + 1
+ if g is None and '\0' in line:
+ # Check for binary files
+ err(filename + ': contains null bytes; not fixed\n')
+ f.close()
+ return 1
+ if lineno == 1 and g is None and line[:2] == '#!':
+ # Check for non-Python scripts
+ words = string.split(line[2:])
+ if words and re.search('[pP]ython', words[0]) < 0:
+ msg = filename + ': ' + words[0]
+ msg = msg + ' script; not fixed\n'
+ err(msg)
+ f.close()
+ return 1
+ while line[-2:] == '\\\n':
+ nextline = f.readline()
+ if not nextline: break
+ line = line + nextline
+ lineno = lineno + 1
+ newline = fixline(line)
+ if newline != line:
+ if g is None:
+ try:
+ g = open(tempname, 'w')
+ except IOError, msg:
+ f.close()
+ err('%s: cannot create: %r\n' % (tempname, msg))
+ return 1
+ f.seek(0)
+ lineno = 0
+ rep(filename + ':\n')
+ continue # restart from the beginning
+ rep(repr(lineno) + '\n')
+ rep('< ' + line)
+ rep('> ' + newline)
+ if g is not None:
+ g.write(newline)
+
+ # End of file
+ f.close()
+ if not g: return 0 # No changes
+
+ # Finishing touch -- move files
+
+ # First copy the file's mode to the temp file
+ try:
+ statbuf = os.stat(filename)
+ os.chmod(tempname, statbuf[ST_MODE] & 07777)
+ except os.error, msg:
+ err('%s: warning: chmod failed (%r)\n' % (tempname, msg))
+ # Then make a backup of the original file as filename~
+ try:
+ os.rename(filename, filename + '~')
+ except os.error, msg:
+ err('%s: warning: backup failed (%r)\n' % (filename, msg))
+ # Now move the temp file to the original file
+ try:
+ os.rename(tempname, filename)
+ except os.error, msg:
+ err('%s: rename failed (%r)\n' % (filename, msg))
+ return 1
+ # Return succes
+ return 0
+
+
+from tokenize import tokenprog
+
+match = {'if':':', 'elif':':', 'while':':', 'return':'\n', \
+ '(':')', '[':']', '{':'}', '`':'`'}
+
+def fixline(line):
+ # Quick check for easy case
+ if '=' not in line: return line
+
+ i, n = 0, len(line)
+ stack = []
+ while i < n:
+ j = tokenprog.match(line, i)
+ if j < 0:
+ # A bad token; forget about the rest of this line
+ print '(Syntax error:)'
+ print line,
+ return line
+ a, b = tokenprog.regs[3] # Location of the token proper
+ token = line[a:b]
+ i = i+j
+ if stack and token == stack[-1]:
+ del stack[-1]
+ elif match.has_key(token):
+ stack.append(match[token])
+ elif token == '=' and stack:
+ line = line[:a] + '==' + line[b:]
+ i, n = a + len('=='), len(line)
+ elif token == '==' and not stack:
+ print '(Warning: \'==\' at top level:)'
+ print line,
+ return line
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/fact.py b/sys/src/cmd/python/Demo/scripts/fact.py
new file mode 100755
index 000000000..03cab8bb8
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/fact.py
@@ -0,0 +1,49 @@
+#! /usr/bin/env python
+
+# Factorize numbers.
+# The algorithm is not efficient, but easy to understand.
+# If there are large factors, it will take forever to find them,
+# because we try all odd numbers between 3 and sqrt(n)...
+
+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 = sqrt(float(n+1))
+ i = 3
+ while i <= limit:
+ if n%i == 0:
+ res.append(i)
+ n = n/i
+ limit = sqrt(n+1)
+ else:
+ i = i+2
+ if n != 1:
+ res.append(n)
+ return res
+
+def main():
+ if len(sys.argv) > 1:
+ for arg in sys.argv[1:]:
+ n = eval(arg)
+ print n, fact(n)
+ else:
+ try:
+ while 1:
+ n = input()
+ print n, fact(n)
+ except EOFError:
+ pass
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/find-uname.py b/sys/src/cmd/python/Demo/scripts/find-uname.py
new file mode 100644
index 000000000..b76b9f0fe
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/find-uname.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+"""
+For each argument on the command line, look for it in the set of all Unicode
+names. Arguments are treated as case-insensitive regular expressions, e.g.:
+
+ % find-uname 'small letter a$' 'horizontal line'
+ *** small letter a$ matches ***
+ LATIN SMALL LETTER A (97)
+ COMBINING LATIN SMALL LETTER A (867)
+ CYRILLIC SMALL LETTER A (1072)
+ PARENTHESIZED LATIN SMALL LETTER A (9372)
+ CIRCLED LATIN SMALL LETTER A (9424)
+ FULLWIDTH LATIN SMALL LETTER A (65345)
+ *** horizontal line matches ***
+ HORIZONTAL LINE EXTENSION (9135)
+"""
+
+import unicodedata
+import sys
+import re
+
+def main(args):
+ unicode_names= []
+ for ix in range(sys.maxunicode+1):
+ try:
+ unicode_names.append( (ix, unicodedata.name(unichr(ix))) )
+ except ValueError: # no name for the character
+ pass
+ for arg in args:
+ pat = re.compile(arg, re.I)
+ matches = [(x,y) for (x,y) in unicode_names
+ if pat.search(y) is not None]
+ if matches:
+ print "***", arg, "matches", "***"
+ for (x,y) in matches:
+ print "%s (%d)" % (y,x)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/sys/src/cmd/python/Demo/scripts/from.py b/sys/src/cmd/python/Demo/scripts/from.py
new file mode 100755
index 000000000..3c04fcd49
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/from.py
@@ -0,0 +1,35 @@
+#! /usr/bin/env 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 sys, os
+
+# Open mailbox file. Exits with exception when this fails.
+
+try:
+ mailbox = os.environ['MAIL']
+except (AttributeError, KeyError):
+ sys.stderr.write('No environment variable $MAIL\n')
+ sys.exit(2)
+
+try:
+ mail = open(mailbox)
+except IOError:
+ sys.exit('Cannot open mailbox file: ' + mailbox)
+
+while 1:
+ line = mail.readline()
+ if not line:
+ break # EOF
+ if line.startswith('From '):
+ # Start of message found
+ print line[:-1],
+ while 1:
+ line = mail.readline()
+ if not line or line == '\n':
+ break
+ if line.startswith('Subject: '):
+ print repr(line[9:-1]),
+ print
diff --git a/sys/src/cmd/python/Demo/scripts/ftpstats.py b/sys/src/cmd/python/Demo/scripts/ftpstats.py
new file mode 100755
index 000000000..5c1599e66
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/ftpstats.py
@@ -0,0 +1,145 @@
+#! /usr/bin/env python
+
+# Extract statistics from ftp daemon log.
+
+# Usage:
+# ftpstats [-m maxitems] [-s search] [file]
+# -m maxitems: restrict number of items in "top-N" lists, default 25.
+# -s string: restrict statistics to lines containing this string.
+# Default file is /usr/adm/ftpd; a "-" means read standard input.
+
+# The script must be run on the host where the ftp daemon runs.
+# (At CWI this is currently buizerd.)
+
+import os
+import sys
+import re
+import string
+import getopt
+
+pat = '^([a-zA-Z0-9 :]*)!(.*)!(.*)!([<>].*)!([0-9]+)!([0-9]+)$'
+prog = re.compile(pat)
+
+def main():
+ maxitems = 25
+ search = None
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'm:s:')
+ except getopt.error, msg:
+ print msg
+ print 'usage: ftpstats [-m maxitems] [file]'
+ sys.exit(2)
+ for o, a in opts:
+ if o == '-m':
+ maxitems = string.atoi(a)
+ if o == '-s':
+ search = a
+ file = '/usr/adm/ftpd'
+ if args: file = args[0]
+ if file == '-':
+ f = sys.stdin
+ else:
+ try:
+ f = open(file, 'r')
+ except IOError, msg:
+ print file, ':', msg
+ sys.exit(1)
+ bydate = {}
+ bytime = {}
+ byfile = {}
+ bydir = {}
+ byhost = {}
+ byuser = {}
+ bytype = {}
+ lineno = 0
+ try:
+ while 1:
+ line = f.readline()
+ if not line: break
+ lineno = lineno + 1
+ if search and string.find(line, search) < 0:
+ continue
+ if prog.match(line) < 0:
+ print 'Bad line', lineno, ':', repr(line)
+ continue
+ items = prog.group(1, 2, 3, 4, 5, 6)
+ (logtime, loguser, loghost, logfile, logbytes,
+ logxxx2) = items
+## print logtime
+## print '-->', loguser
+## print '--> -->', loghost
+## print '--> --> -->', logfile
+## print '--> --> --> -->', logbytes
+## print '--> --> --> --> -->', logxxx2
+## for i in logtime, loghost, logbytes, logxxx2:
+## if '!' in i: print '???', i
+ add(bydate, logtime[-4:] + ' ' + logtime[:6], items)
+ add(bytime, logtime[7:9] + ':00-59', items)
+ direction, logfile = logfile[0], logfile[1:]
+ # The real path probably starts at the last //...
+ while 1:
+ i = string.find(logfile, '//')
+ if i < 0: break
+ logfile = logfile[i+1:]
+ add(byfile, logfile + ' ' + direction, items)
+ logdir = os.path.dirname(logfile)
+## logdir = os.path.normpath(logdir) + '/.'
+ while 1:
+ add(bydir, logdir + ' ' + direction, items)
+ dirhead = os.path.dirname(logdir)
+ if dirhead == logdir: break
+ logdir = dirhead
+ add(byhost, loghost, items)
+ add(byuser, loguser, items)
+ add(bytype, direction, items)
+ except KeyboardInterrupt:
+ print 'Interrupted at line', lineno
+ show(bytype, 'by transfer direction', maxitems)
+ show(bydir, 'by directory', maxitems)
+ show(byfile, 'by file', maxitems)
+ show(byhost, 'by host', maxitems)
+ show(byuser, 'by user', maxitems)
+ showbar(bydate, 'by date')
+ showbar(bytime, 'by time of day')
+
+def showbar(dict, title):
+ n = len(title)
+ print '='*((70-n)/2), title, '='*((71-n)/2)
+ list = []
+ keys = dict.keys()
+ keys.sort()
+ for key in keys:
+ n = len(str(key))
+ list.append((len(dict[key]), key))
+ maxkeylength = 0
+ maxcount = 0
+ for count, key in list:
+ maxkeylength = max(maxkeylength, len(key))
+ maxcount = max(maxcount, count)
+ maxbarlength = 72 - maxkeylength - 7
+ for count, key in list:
+ barlength = int(round(maxbarlength*float(count)/maxcount))
+ bar = '*'*barlength
+ print '%5d %-*s %s' % (count, maxkeylength, key, bar)
+
+def show(dict, title, maxitems):
+ if len(dict) > maxitems:
+ title = title + ' (first %d)'%maxitems
+ n = len(title)
+ print '='*((70-n)/2), title, '='*((71-n)/2)
+ list = []
+ keys = dict.keys()
+ for key in keys:
+ list.append((-len(dict[key]), key))
+ list.sort()
+ for count, key in list[:maxitems]:
+ print '%5d %s' % (-count, key)
+
+def add(dict, key, item):
+ if dict.has_key(key):
+ dict[key].append(item)
+ else:
+ dict[key] = [item]
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/lpwatch.py b/sys/src/cmd/python/Demo/scripts/lpwatch.py
new file mode 100755
index 000000000..8887dee7d
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/lpwatch.py
@@ -0,0 +1,110 @@
+#! /usr/bin/env 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 printers:
+ # Strip '-P' from printer names just in case
+ # the user specified it...
+ for i in range(len(printers)):
+ if printers[i][:2] == '-P':
+ printers[i] = printers[i][2:]
+ else:
+ if posix.environ.has_key('PRINTER'):
+ printers = [posix.environ['PRINTER']]
+ else:
+ printers = [DEF_PRINTER]
+ #
+ clearhome = posix.popen('clear', 'r').read()
+ #
+ while 1:
+ text = clearhome
+ for name in printers:
+ text = text + makestatus(name, thisuser) + '\n'
+ print text
+ time.sleep(delay)
+
+def makestatus(name, thisuser):
+ pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r')
+ lines = []
+ 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':
+ line = string.strip(line)
+ if line == 'no entries':
+ line = name + ': idle'
+ elif line[-22:] == ' is ready and printing':
+ line = name
+ lines.append(line)
+ #
+ if totaljobs:
+ line = '%d K' % ((totalbytes+1023)/1024)
+ if totaljobs <> len(users):
+ line = line + ' (%d jobs)' % totaljobs
+ if len(users) == 1:
+ line = line + ' for %s' % (users.keys()[0],)
+ else:
+ line = line + ' for %d users' % len(users)
+ if userseen:
+ if aheadjobs == 0:
+ line = line + ' (%s first)' % thisuser
+ else:
+ line = line + ' (%d K before %s)' % (
+ (aheadbytes+1023)/1024, thisuser)
+ lines.append(line)
+ #
+ sts = pipe.close()
+ if sts:
+ lines.append('lpq exit status %r' % (sts,))
+ return string.joinfields(lines, ': ')
+
+if __name__ == "__main__":
+ try:
+ main()
+ except KeyboardInterrupt:
+ pass
diff --git a/sys/src/cmd/python/Demo/scripts/makedir.py b/sys/src/cmd/python/Demo/scripts/makedir.py
new file mode 100755
index 000000000..f70facd08
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/makedir.py
@@ -0,0 +1,21 @@
+#! /usr/bin/env python
+
+# Like mkdir, but also make intermediate directories if necessary.
+# It is not an error if the given directory already exists (as long
+# as it is a directory).
+# Errors are not treated specially -- you just get a Python exception.
+
+import sys, os
+
+def main():
+ for p in sys.argv[1:]:
+ makedirs(p)
+
+def makedirs(p):
+ if p and not os.path.isdir(p):
+ head, tail = os.path.split(p)
+ makedirs(head)
+ os.mkdir(p, 0777)
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/markov.py b/sys/src/cmd/python/Demo/scripts/markov.py
new file mode 100755
index 000000000..bddec5693
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/markov.py
@@ -0,0 +1,117 @@
+#! /usr/bin/env python
+
+class Markov:
+ def __init__(self, histsize, choice):
+ self.histsize = histsize
+ self.choice = choice
+ self.trans = {}
+ def add(self, state, next):
+ if not self.trans.has_key(state):
+ self.trans[state] = [next]
+ else:
+ self.trans[state].append(next)
+ def put(self, seq):
+ n = self.histsize
+ add = self.add
+ add(None, seq[:0])
+ for i in range(len(seq)):
+ add(seq[max(0, i-n):i], seq[i:i+1])
+ add(seq[len(seq)-n:], None)
+ def get(self):
+ choice = self.choice
+ trans = self.trans
+ n = self.histsize
+ seq = choice(trans[None])
+ while 1:
+ subseq = seq[max(0, len(seq)-n):]
+ options = trans[subseq]
+ next = choice(options)
+ if not next: break
+ seq = seq + next
+ return seq
+
+def test():
+ import sys, string, random, getopt
+ args = sys.argv[1:]
+ try:
+ opts, args = getopt.getopt(args, '0123456789cdw')
+ except getopt.error:
+ print 'Usage: markov [-#] [-cddqw] [file] ...'
+ print 'Options:'
+ print '-#: 1-digit history size (default 2)'
+ print '-c: characters (default)'
+ print '-w: words'
+ print '-d: more debugging output'
+ print '-q: no debugging output'
+ print 'Input files (default stdin) are split in paragraphs'
+ print 'separated blank lines and each paragraph is split'
+ print 'in words by whitespace, then reconcatenated with'
+ print 'exactly one space separating words.'
+ print 'Output consists of paragraphs separated by blank'
+ print 'lines, where lines are no longer than 72 characters.'
+ histsize = 2
+ do_words = 0
+ debug = 1
+ for o, a in opts:
+ if '-0' <= o <= '-9': histsize = eval(o[1:])
+ if o == '-c': do_words = 0
+ if o == '-d': debug = debug + 1
+ if o == '-q': debug = 0
+ if o == '-w': do_words = 1
+ if not args: args = ['-']
+ m = Markov(histsize, random.choice)
+ try:
+ for filename in args:
+ if filename == '-':
+ f = sys.stdin
+ if f.isatty():
+ print 'Sorry, need stdin from file'
+ continue
+ else:
+ f = open(filename, 'r')
+ if debug: print 'processing', filename, '...'
+ text = f.read()
+ f.close()
+ paralist = string.splitfields(text, '\n\n')
+ for para in paralist:
+ if debug > 1: print 'feeding ...'
+ words = string.split(para)
+ if words:
+ if do_words: data = tuple(words)
+ else: data = string.joinfields(words, ' ')
+ m.put(data)
+ except KeyboardInterrupt:
+ print 'Interrupted -- continue with data read so far'
+ if not m.trans:
+ print 'No valid input files'
+ return
+ if debug: print 'done.'
+ if debug > 1:
+ for key in m.trans.keys():
+ if key is None or len(key) < histsize:
+ print repr(key), m.trans[key]
+ if histsize == 0: print repr(''), m.trans['']
+ print
+ while 1:
+ data = m.get()
+ if do_words: words = data
+ else: words = string.split(data)
+ n = 0
+ limit = 72
+ for w in words:
+ if n + len(w) > limit:
+ print
+ n = 0
+ print w,
+ n = n + len(w) + 1
+ print
+ print
+
+def tuple(list):
+ if len(list) == 0: return ()
+ if len(list) == 1: return (list[0],)
+ i = len(list)/2
+ return tuple(list[:i]) + tuple(list[i:])
+
+if __name__ == "__main__":
+ test()
diff --git a/sys/src/cmd/python/Demo/scripts/mboxconvert.py b/sys/src/cmd/python/Demo/scripts/mboxconvert.py
new file mode 100755
index 000000000..8c462f3b1
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/mboxconvert.py
@@ -0,0 +1,124 @@
+#! /usr/bin/env python
+
+# Convert MH directories (1 message per file) or MMDF mailboxes (4x^A
+# delimited) to unix mailbox (From ... delimited) on stdout.
+# If -f is given, files contain one message per file (e.g. MH messages)
+
+import rfc822
+import sys
+import time
+import os
+import stat
+import getopt
+import re
+
+def main():
+ dofile = mmdf
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'f')
+ except getopt.error, msg:
+ sys.stderr.write('%s\n' % msg)
+ sys.exit(2)
+ for o, a in opts:
+ if o == '-f':
+ dofile = message
+ if not args:
+ args = ['-']
+ sts = 0
+ for arg in args:
+ if arg == '-' or arg == '':
+ sts = dofile(sys.stdin) or sts
+ elif os.path.isdir(arg):
+ sts = mh(arg) or sts
+ elif os.path.isfile(arg):
+ try:
+ f = open(arg)
+ except IOError, msg:
+ sys.stderr.write('%s: %s\n' % (arg, msg))
+ sts = 1
+ continue
+ sts = dofile(f) or sts
+ f.close()
+ else:
+ sys.stderr.write('%s: not found\n' % arg)
+ sts = 1
+ if sts:
+ sys.exit(sts)
+
+numeric = re.compile('[1-9][0-9]*')
+
+def mh(dir):
+ sts = 0
+ msgs = os.listdir(dir)
+ for msg in msgs:
+ if numeric.match(msg) != len(msg):
+ continue
+ fn = os.path.join(dir, msg)
+ try:
+ f = open(fn)
+ except IOError, msg:
+ sys.stderr.write('%s: %s\n' % (fn, msg))
+ sts = 1
+ continue
+ sts = message(f) or sts
+ return sts
+
+def mmdf(f):
+ sts = 0
+ while 1:
+ line = f.readline()
+ if not line:
+ break
+ if line == '\1\1\1\1\n':
+ sts = message(f, line) or sts
+ else:
+ sys.stderr.write(
+ 'Bad line in MMFD mailbox: %r\n' % (line,))
+ return sts
+
+counter = 0 # for generating unique Message-ID headers
+
+def message(f, delimiter = ''):
+ sts = 0
+ # Parse RFC822 header
+ m = rfc822.Message(f)
+ # Write unix header line
+ fullname, email = m.getaddr('From')
+ tt = m.getdate('Date')
+ if tt:
+ t = time.mktime(tt)
+ else:
+ sys.stderr.write(
+ 'Unparseable date: %r\n' % (m.getheader('Date'),))
+ t = os.fstat(f.fileno())[stat.ST_MTIME]
+ print 'From', email, time.ctime(t)
+ # Copy RFC822 header
+ for line in m.headers:
+ print line,
+ # Invent Message-ID header if none is present
+ if not m.has_key('message-id'):
+ global counter
+ counter = counter + 1
+ msgid = "<%s.%d>" % (hex(t), counter)
+ sys.stderr.write("Adding Message-ID %s (From %s)\n" %
+ (msgid, email))
+ print "Message-ID:", msgid
+ print
+ # Copy body
+ while 1:
+ line = f.readline()
+ if line == delimiter:
+ break
+ if not line:
+ sys.stderr.write('Unexpected EOF in message\n')
+ sts = 1
+ break
+ if line[:5] == 'From ':
+ line = '>' + line
+ print line,
+ # Print trailing newline
+ print
+ return sts
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/mkrcs.py b/sys/src/cmd/python/Demo/scripts/mkrcs.py
new file mode 100755
index 000000000..cacdda0a5
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/mkrcs.py
@@ -0,0 +1,61 @@
+#! /usr/bin/env python
+
+# A rather specialized script to make sure that a symbolic link named
+# RCS exists pointing to a real RCS directory in a parallel tree
+# referenced as RCStree in an ancestor directory.
+# (I use this because I like my RCS files to reside on a physically
+# different machine).
+
+import os
+
+def main():
+ rcstree = 'RCStree'
+ rcs = 'RCS'
+ if os.path.islink(rcs):
+ print '%r is a symlink to %r' % (rcs, os.readlink(rcs))
+ return
+ if os.path.isdir(rcs):
+ print '%r is an ordinary directory' % (rcs,)
+ return
+ if os.path.exists(rcs):
+ print '%r is a file?!?!' % (rcs,)
+ return
+ #
+ p = os.getcwd()
+ up = ''
+ down = ''
+ # Invariants:
+ # (1) join(p, down) is the current directory
+ # (2) up is the same directory as p
+ # Ergo:
+ # (3) join(up, down) is the current directory
+ #print 'p =', repr(p)
+ while not os.path.isdir(os.path.join(p, rcstree)):
+ head, tail = os.path.split(p)
+ #print 'head = %r; tail = %r' % (head, tail)
+ if not tail:
+ print 'Sorry, no ancestor dir contains %r' % (rcstree,)
+ return
+ p = head
+ up = os.path.join(os.pardir, up)
+ down = os.path.join(tail, down)
+ #print 'p = %r; up = %r; down = %r' % (p, up, down)
+ there = os.path.join(up, rcstree)
+ there = os.path.join(there, down)
+ there = os.path.join(there, rcs)
+ if os.path.isdir(there):
+ print '%r already exists' % (there, )
+ else:
+ print 'making %r' % (there,)
+ makedirs(there)
+ print 'making symlink %r -> %r' % (rcs, there)
+ os.symlink(there, rcs)
+
+def makedirs(p):
+ if not os.path.isdir(p):
+ head, tail = os.path.split(p)
+ makedirs(head)
+ os.mkdir(p, 0777)
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/morse.py b/sys/src/cmd/python/Demo/scripts/morse.py
new file mode 100755
index 000000000..3da49da9e
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/morse.py
@@ -0,0 +1,149 @@
+# DAH should be three DOTs.
+# Space between DOTs and DAHs should be one DOT.
+# Space between two letters should be one DAH.
+# Space between two words should be DOT DAH DAH.
+
+import sys, math, audiodev
+
+DOT = 30
+DAH = 3 * DOT
+OCTAVE = 2 # 1 == 441 Hz, 2 == 882 Hz, ...
+
+morsetab = {
+ 'A': '.-', 'a': '.-',
+ 'B': '-...', 'b': '-...',
+ 'C': '-.-.', 'c': '-.-.',
+ 'D': '-..', 'd': '-..',
+ 'E': '.', 'e': '.',
+ 'F': '..-.', 'f': '..-.',
+ 'G': '--.', 'g': '--.',
+ 'H': '....', 'h': '....',
+ 'I': '..', 'i': '..',
+ 'J': '.---', 'j': '.---',
+ 'K': '-.-', 'k': '-.-',
+ 'L': '.-..', 'l': '.-..',
+ 'M': '--', 'm': '--',
+ 'N': '-.', 'n': '-.',
+ 'O': '---', 'o': '---',
+ 'P': '.--.', 'p': '.--.',
+ 'Q': '--.-', 'q': '--.-',
+ 'R': '.-.', 'r': '.-.',
+ 'S': '...', 's': '...',
+ 'T': '-', 't': '-',
+ 'U': '..-', 'u': '..-',
+ 'V': '...-', 'v': '...-',
+ 'W': '.--', 'w': '.--',
+ 'X': '-..-', 'x': '-..-',
+ 'Y': '-.--', 'y': '-.--',
+ 'Z': '--..', 'z': '--..',
+ '0': '-----',
+ '1': '.----',
+ '2': '..---',
+ '3': '...--',
+ '4': '....-',
+ '5': '.....',
+ '6': '-....',
+ '7': '--...',
+ '8': '---..',
+ '9': '----.',
+ ',': '--..--',
+ '.': '.-.-.-',
+ '?': '..--..',
+ ';': '-.-.-.',
+ ':': '---...',
+ "'": '.----.',
+ '-': '-....-',
+ '/': '-..-.',
+ '(': '-.--.-',
+ ')': '-.--.-',
+ '_': '..--.-',
+ ' ': ' '
+}
+
+# If we play at 44.1 kHz (which we do), then if we produce one sine
+# wave in 100 samples, we get a tone of 441 Hz. If we produce two
+# sine waves in these 100 samples, we get a tone of 882 Hz. 882 Hz
+# appears to be a nice one for playing morse code.
+def mkwave(octave):
+ global sinewave, nowave
+ sinewave = ''
+ for i in range(100):
+ val = int(math.sin(math.pi * float(i) * octave / 50.0) * 30000)
+ sinewave = sinewave + chr((val >> 8) & 255) + chr(val & 255)
+ nowave = '\0' * 200
+
+mkwave(OCTAVE)
+
+def main():
+ import getopt, string
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'o:p:')
+ except getopt.error:
+ sys.stderr.write('Usage ' + sys.argv[0] +
+ ' [ -o outfile ] [ args ] ...\n')
+ sys.exit(1)
+ dev = None
+ for o, a in opts:
+ if o == '-o':
+ import aifc
+ dev = aifc.open(a, 'w')
+ dev.setframerate(44100)
+ dev.setsampwidth(2)
+ dev.setnchannels(1)
+ if o == '-p':
+ mkwave(string.atoi(a))
+ if not dev:
+ import audiodev
+ dev = audiodev.AudioDev()
+ dev.setoutrate(44100)
+ dev.setsampwidth(2)
+ dev.setnchannels(1)
+ dev.close = dev.stop
+ dev.writeframesraw = dev.writeframes
+ if args:
+ line = string.join(args)
+ else:
+ line = sys.stdin.readline()
+ while line:
+ mline = morse(line)
+ play(mline, dev)
+ if hasattr(dev, 'wait'):
+ dev.wait()
+ if not args:
+ line = sys.stdin.readline()
+ else:
+ line = ''
+ dev.close()
+
+# Convert a string to morse code with \001 between the characters in
+# the string.
+def morse(line):
+ res = ''
+ for c in line:
+ try:
+ res = res + morsetab[c] + '\001'
+ except KeyError:
+ pass
+ return res
+
+# Play a line of morse code.
+def play(line, dev):
+ for c in line:
+ if c == '.':
+ sine(dev, DOT)
+ elif c == '-':
+ sine(dev, DAH)
+ else: # space
+ pause(dev, DAH + DOT)
+ pause(dev, DOT)
+
+def sine(dev, length):
+ for i in range(length):
+ dev.writeframesraw(sinewave)
+
+def pause(dev, length):
+ for i in range(length):
+ dev.writeframesraw(nowave)
+
+if __name__ == '__main__' or sys.argv[0] == __name__:
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/newslist.doc b/sys/src/cmd/python/Demo/scripts/newslist.doc
new file mode 100755
index 000000000..87fd9ba27
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/newslist.doc
@@ -0,0 +1,59 @@
+ NEWSLIST
+ ========
+ A program to assist HTTP browsing of newsgroups
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+WWW browsers such as NCSA Mosaic allow the user to read newsgroup
+articles by specifying the group name in a URL eg 'news:comp.answers'.
+
+To browse through many groups, though, (and there are several thousand
+of them) you really need a page or pages containing links to all the
+groups. There are some good ones out there, for example,
+
+ http://info.cern.ch/hypertext/DataSources/News/Groups/Overview.html
+
+is the standard one at CERN, but it only shows the groups available there,
+which may be rather different from those available on your machine.
+
+Newslist is a program which creates a hierarchy of pages for you based
+on the groups available from YOUR server. It is written in python - a
+splendid interpreted object-oriented language which I suggest you get
+right now from the directory /pub/python at ftp.cwi.nl, if you haven't
+already got it.
+
+You should be able to see some sample output by looking at:
+ http://pelican.cl.cam.ac.uk/newspage/root.html
+
+Descriptions of newsgroups can be added from a file with one group
+per line. eg:
+
+ alt.foo Articles about foo
+ comp.bar Programming in 'bar' and related languages
+
+A suitable list detailing most groups can be found at ftp.uu.net in
+/uunet-info/newsgroups.gz.
+
+Make sure you read the information at the beginning of the program source and
+configure the variables before running.
+
+In addition to python, you need:
+
+ An NNTP-based news feed.
+ A directory in which to put the pages.
+
+The programming is not very beautiful, but it works! It comes with no
+warranty, express or implied, but with the hope that some others may
+find it useful.
+
+Comments, improvements & suggestions welcomed.
+Quentin Stafford-Fraser
+
+ ----------------------------------------------------------------------
+ Quentin Stafford-Fraser
+ http://pelican.cl.cam.ac.uk/people/qs101/me.html
+
+ Cambridge University Computer Lab Rank Xerox Cambridge EuroPARC
+ qs101@cl.cam.ac.uk fraser@europarc.xerox.com
+ Tel: +44 223 334411 Tel: +44 223 341521
+ Fax: +44 223 334679 Fax: +44 223 341510
+ ----------------------------------------------------------------------
diff --git a/sys/src/cmd/python/Demo/scripts/newslist.py b/sys/src/cmd/python/Demo/scripts/newslist.py
new file mode 100755
index 000000000..35ba48a48
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/newslist.py
@@ -0,0 +1,366 @@
+#! /usr/bin/env python
+#######################################################################
+# Newslist $Revision: 37320 $
+#
+# Syntax:
+# newslist [ -a ]
+#
+# This is a program to create a directory full of HTML pages
+# which between them contain links to all the newsgroups available
+# on your server.
+#
+# The -a option causes a complete list of all groups to be read from
+# the server rather than just the ones which have appeared since last
+# execution. This recreates the local list from scratch. Use this on
+# the first invocation of the program, and from time to time thereafter.
+# When new groups are first created they may appear on your server as
+# empty groups. By default, empty groups are ignored by the -a option.
+# However, these new groups will not be created again, and so will not
+# appear in the server's list of 'new groups' at a later date. Hence it
+# won't appear until you do a '-a' after some articles have appeared.
+#
+# I should really keep a list of ignored empty groups and re-check them
+# for articles on every run, but I haven't got around to it yet.
+#
+# This assumes an NNTP news feed.
+#
+# Feel free to copy, distribute and modify this code for
+# non-commercial use. If you make any useful modifications, let me
+# know!
+#
+# (c) Quentin Stafford-Fraser 1994
+# fraser@europarc.xerox.com qs101@cl.cam.ac.uk
+# #
+#######################################################################
+import sys,nntplib, string, marshal, time, os, posix, string
+
+#######################################################################
+# Check these variables before running! #
+
+# Top directory.
+# Filenames which don't start with / are taken as being relative to this.
+topdir='/anfs/qsbigdisc/web/html/newspage'
+
+# The name of your NNTP host
+# eg.
+# newshost = 'nntp-serv.cl.cam.ac.uk'
+# or use following to get the name from the NNTPSERVER environment
+# variable:
+# newshost = posix.environ['NNTPSERVER']
+newshost = 'nntp-serv.cl.cam.ac.uk'
+
+# The filename for a local cache of the newsgroup list
+treefile = 'grouptree'
+
+# The filename for descriptions of newsgroups
+# I found a suitable one at ftp.uu.net in /uunet-info/newgroups.gz
+# You can set this to '' if you don't wish to use one.
+descfile = 'newsgroups'
+
+# The directory in which HTML pages should be created
+# eg.
+# pagedir = '/usr/local/lib/html/newspage'
+# pagedir = 'pages'
+pagedir = topdir
+
+# The html prefix which will refer to this directory
+# eg.
+# httppref = '/newspage/',
+# or leave blank for relative links between pages: (Recommended)
+# httppref = ''
+httppref = ''
+
+# The name of the 'root' news page in this directory.
+# A .html suffix will be added.
+rootpage = 'root'
+
+# Set skipempty to 0 if you wish to see links to empty groups as well.
+# Only affects the -a option.
+skipempty = 1
+
+# pagelinkicon can contain html to put an icon after links to
+# further pages. This helps to make important links stand out.
+# Set to '' if not wanted, or '...' is quite a good one.
+pagelinkicon='... <img src="http://pelican.cl.cam.ac.uk/icons/page.xbm"> '
+
+# ---------------------------------------------------------------------
+# Less important personal preferences:
+
+# Sublistsize controls the maximum number of items the will appear as
+# an indented sub-list before the whole thing is moved onto a different
+# page. The smaller this is, the more pages you will have, but the
+# shorter each will be.
+sublistsize = 4
+
+# That should be all. #
+#######################################################################
+
+for dir in os.curdir, os.environ['HOME']:
+ rcfile = os.path.join(dir, '.newslistrc.py')
+ if os.path.exists(rcfile):
+ print rcfile
+ execfile(rcfile)
+ break
+
+from nntplib import NNTP
+from stat import *
+
+rcsrev = '$Revision: 37320 $'
+rcsrev = string.join(filter(lambda s: '$' not in s, string.split(rcsrev)))
+desc = {}
+
+# Make (possibly) relative filenames into absolute ones
+treefile = os.path.join(topdir,treefile)
+descfile = os.path.join(topdir,descfile)
+page = os.path.join(topdir,pagedir)
+
+# First the bits for creating trees ---------------------------
+
+# Addtotree creates/augments a tree from a list of group names
+def addtotree(tree, groups):
+ print 'Updating tree...'
+ for i in groups:
+ parts = string.splitfields(i,'.')
+ makeleaf(tree, parts)
+
+# Makeleaf makes a leaf and the branch leading to it if necessary
+def makeleaf(tree,path):
+ j = path[0]
+ l = len(path)
+
+ if not tree.has_key(j):
+ tree[j] = {}
+ if l == 1:
+ tree[j]['.'] = '.'
+ if l > 1:
+ makeleaf(tree[j],path[1:])
+
+# Then the bits for outputting trees as pages ----------------
+
+# Createpage creates an HTML file named <root>.html containing links
+# to those groups beginning with <root>.
+
+def createpage(root, tree, p):
+ filename = os.path.join(pagedir,root+'.html')
+ if root == rootpage:
+ detail = ''
+ else:
+ detail = ' under ' + root
+ f = open(filename,'w')
+ # f.write('Content-Type: text/html\n')
+ f.write('<TITLE>Newsgroups available' + detail + '</TITLE>\n')
+ f.write('<H1>Newsgroups available' + detail +'</H1>\n')
+ f.write('<A HREF="'+httppref+rootpage+'.html">Back to top level</A><P>\n')
+ printtree(f,tree,0,p)
+ f.write('<I>This page automatically created by \'newslist\' v. '+rcsrev+'.')
+ f.write(time.ctime(time.time()) + '</I><P>')
+ f.close()
+
+# Printtree prints the groups as a bulleted list. Groups with
+# more than <sublistsize> subgroups will be put on a separate page.
+# Other sets of subgroups are just indented.
+
+def printtree(f, tree, indent, p):
+ global desc
+ l = len(tree)
+
+ if l > sublistsize and indent>0:
+ # Create a new page and a link to it
+ f.write('<LI><B><A HREF="'+httppref+p[1:]+'.html">')
+ f.write(p[1:]+'.*')
+ f.write('</A></B>'+pagelinkicon+'\n')
+ createpage(p[1:], tree, p)
+ return
+
+ kl = tree.keys()
+
+ if l > 1:
+ kl.sort()
+ if indent > 0:
+ # Create a sub-list
+ f.write('<LI>'+p[1:]+'\n<UL>')
+ else:
+ # Create a main list
+ f.write('<UL>')
+ indent = indent + 1
+
+ for i in kl:
+ if i == '.':
+ # Output a newsgroup
+ f.write('<LI><A HREF="news:' + p[1:] + '">'+ p[1:] + '</A> ')
+ if desc.has_key(p[1:]):
+ f.write(' <I>'+desc[p[1:]]+'</I>\n')
+ else:
+ f.write('\n')
+ else:
+ # Output a hierarchy
+ printtree(f,tree[i], indent, p+'.'+i)
+
+ if l > 1:
+ f.write('\n</UL>')
+
+# Reading descriptions file ---------------------------------------
+
+# This returns an array mapping group name to its description
+
+def readdesc(descfile):
+ global desc
+
+ desc = {}
+
+ if descfile == '':
+ return
+
+ try:
+ d = open(descfile, 'r')
+ print 'Reading descriptions...'
+ except (IOError):
+ print 'Failed to open description file ' + descfile
+ return
+ l = d.readline()
+ while l != '':
+ bits = string.split(l)
+ try:
+ grp = bits[0]
+ dsc = string.join(bits[1:])
+ if len(dsc)>1:
+ desc[grp] = dsc
+ except (IndexError):
+ pass
+ l = d.readline()
+
+# Check that ouput directory exists, ------------------------------
+# and offer to create it if not
+
+def checkopdir(pagedir):
+ if not os.path.isdir(pagedir):
+ print 'Directory '+pagedir+' does not exist.'
+ print 'Shall I create it for you? (y/n)'
+ if sys.stdin.readline()[0] == 'y':
+ try:
+ os.mkdir(pagedir,0777)
+ except:
+ print 'Sorry - failed!'
+ sys.exit(1)
+ else:
+ print 'OK. Exiting.'
+ sys.exit(1)
+
+# Read and write current local tree ----------------------------------
+
+def readlocallist(treefile):
+ print 'Reading current local group list...'
+ tree = {}
+ try:
+ treetime = time.localtime(os.stat(treefile)[ST_MTIME])
+ except:
+ print '\n*** Failed to open local group cache '+treefile
+ print 'If this is the first time you have run newslist, then'
+ print 'use the -a option to create it.'
+ sys.exit(1)
+ treedate = '%02d%02d%02d' % (treetime[0] % 100 ,treetime[1], treetime[2])
+ try:
+ dump = open(treefile,'r')
+ tree = marshal.load(dump)
+ dump.close()
+ except (IOError):
+ print 'Cannot open local group list ' + treefile
+ return (tree, treedate)
+
+def writelocallist(treefile, tree):
+ try:
+ dump = open(treefile,'w')
+ groups = marshal.dump(tree,dump)
+ dump.close()
+ print 'Saved list to '+treefile+'\n'
+ except:
+ print 'Sorry - failed to write to local group cache '+treefile
+ print 'Does it (or its directory) have the correct permissions?'
+ sys.exit(1)
+
+# Return list of all groups on server -----------------------------
+
+def getallgroups(server):
+ print 'Getting list of all groups...'
+ treedate='010101'
+ info = server.list()[1]
+ groups = []
+ print 'Processing...'
+ if skipempty:
+ print '\nIgnoring following empty groups:'
+ for i in info:
+ grpname = string.split(i[0])[0]
+ if skipempty and string.atoi(i[1]) < string.atoi(i[2]):
+ print grpname+' ',
+ else:
+ groups.append(grpname)
+ print '\n'
+ if skipempty:
+ print '(End of empty groups)'
+ return groups
+
+# Return list of new groups on server -----------------------------
+
+def getnewgroups(server, treedate):
+ print 'Getting list of new groups since start of '+treedate+'...',
+ info = server.newgroups(treedate,'000001')[1]
+ print 'got %d.' % len(info)
+ print 'Processing...',
+ groups = []
+ for i in info:
+ grpname = string.split(i)[0]
+ groups.append(grpname)
+ print 'Done'
+ return groups
+
+# Now the main program --------------------------------------------
+
+def main():
+ global desc
+
+ tree={}
+
+ # Check that the output directory exists
+ checkopdir(pagedir);
+
+ try:
+ print 'Connecting to '+newshost+'...'
+ if sys.version[0] == '0':
+ s = NNTP.init(newshost)
+ else:
+ s = NNTP(newshost)
+ connected = 1
+ except (nntplib.error_temp, nntplib.error_perm), x:
+ print 'Error connecting to host:', x
+ print 'I\'ll try to use just the local list.'
+ connected = 0
+
+ # If -a is specified, read the full list of groups from server
+ if connected and len(sys.argv) > 1 and sys.argv[1] == '-a':
+
+ groups = getallgroups(s)
+
+ # Otherwise just read the local file and then add
+ # groups created since local file last modified.
+ else:
+
+ (tree, treedate) = readlocallist(treefile)
+ if connected:
+ groups = getnewgroups(s, treedate)
+
+ if connected:
+ addtotree(tree, groups)
+ writelocallist(treefile,tree)
+
+ # Read group descriptions
+ readdesc(descfile)
+
+ print 'Creating pages...'
+ createpage(rootpage, tree, '')
+ print 'Done'
+
+if __name__ == "__main__":
+ main()
+
+# That's all folks
+######################################################################
diff --git a/sys/src/cmd/python/Demo/scripts/pi.py b/sys/src/cmd/python/Demo/scripts/pi.py
new file mode 100755
index 000000000..9b242451a
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/pi.py
@@ -0,0 +1,34 @@
+#! /usr/bin/env 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
+ while d == d1:
+ output(d)
+ a, a1 = 10L*(a%b), 10L*(a1%b1)
+ d, d1 = a/b, a1/b1
+
+def output(d):
+ # Use write() to avoid spaces between the digits
+ # Use str() to avoid the 'L'
+ sys.stdout.write(str(d))
+ # Flush so the output is seen immediately
+ sys.stdout.flush()
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/pp.py b/sys/src/cmd/python/Demo/scripts/pp.py
new file mode 100755
index 000000000..0491fa9bf
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/pp.py
@@ -0,0 +1,130 @@
+#! /usr/bin/env python
+
+# Emulate some Perl command line options.
+# Usage: pp [-a] [-c] [-d] [-e scriptline] [-F fieldsep] [-n] [-p] [file] ...
+# Where the options mean the following:
+# -a : together with -n or -p, splits each line into list F
+# -c : check syntax only, do not execute any code
+# -d : run the script under the debugger, pdb
+# -e scriptline : gives one line of the Python script; may be repeated
+# -F fieldsep : sets the field separator for the -a option [not in Perl]
+# -n : runs the script for each line of input
+# -p : prints the line after the script has run
+# When no script lines have been passed, the first file argument
+# contains the script. With -n or -p, the remaining arguments are
+# read as input to the script, line by line. If a file is '-'
+# or missing, standard input is read.
+
+# XXX To do:
+# - add -i extension option (change files in place)
+# - make a single loop over the files and lines (changes effect of 'break')?
+# - add an option to specify the record separator
+# - except for -n/-p, run directly from the file if at all possible
+
+import sys
+import string
+import getopt
+
+FS = ''
+SCRIPT = []
+AFLAG = 0
+CFLAG = 0
+DFLAG = 0
+NFLAG = 0
+PFLAG = 0
+
+try:
+ optlist, ARGS = getopt.getopt(sys.argv[1:], 'acde:F:np')
+except getopt.error, msg:
+ sys.stderr.write(sys.argv[0] + ': ' + msg + '\n')
+ sys.exit(2)
+
+for option, optarg in optlist:
+ if option == '-a':
+ AFLAG = 1
+ elif option == '-c':
+ CFLAG = 1
+ elif option == '-d':
+ DFLAG = 1
+ elif option == '-e':
+ for line in string.splitfields(optarg, '\n'):
+ SCRIPT.append(line)
+ elif option == '-F':
+ FS = optarg
+ elif option == '-n':
+ NFLAG = 1
+ PFLAG = 0
+ elif option == '-p':
+ NFLAG = 1
+ PFLAG = 1
+ else:
+ print option, 'not recognized???'
+
+if not ARGS: ARGS.append('-')
+
+if not SCRIPT:
+ if ARGS[0] == '-':
+ fp = sys.stdin
+ else:
+ fp = open(ARGS[0], 'r')
+ while 1:
+ line = fp.readline()
+ if not line: break
+ SCRIPT.append(line[:-1])
+ del fp
+ del ARGS[0]
+ if not ARGS: ARGS.append('-')
+
+if CFLAG:
+ prologue = ['if 0:']
+ epilogue = []
+elif NFLAG:
+ # Note that it is on purpose that AFLAG and PFLAG are
+ # tested dynamically each time through the loop
+ prologue = [ \
+ 'LINECOUNT = 0', \
+ 'for FILE in ARGS:', \
+ ' \tif FILE == \'-\':', \
+ ' \t \tFP = sys.stdin', \
+ ' \telse:', \
+ ' \t \tFP = open(FILE, \'r\')', \
+ ' \tLINENO = 0', \
+ ' \twhile 1:', \
+ ' \t \tLINE = FP.readline()', \
+ ' \t \tif not LINE: break', \
+ ' \t \tLINENO = LINENO + 1', \
+ ' \t \tLINECOUNT = LINECOUNT + 1', \
+ ' \t \tL = LINE[:-1]', \
+ ' \t \taflag = AFLAG', \
+ ' \t \tif aflag:', \
+ ' \t \t \tif FS: F = string.splitfields(L, FS)', \
+ ' \t \t \telse: F = string.split(L)' \
+ ]
+ epilogue = [ \
+ ' \t \tif not PFLAG: continue', \
+ ' \t \tif aflag:', \
+ ' \t \t \tif FS: print string.joinfields(F, FS)', \
+ ' \t \t \telse: print string.join(F)', \
+ ' \t \telse: print L', \
+ ]
+else:
+ prologue = ['if 1:']
+ epilogue = []
+
+# Note that we indent using tabs only, so that any indentation style
+# used in 'command' will come out right after re-indentation.
+
+program = string.joinfields(prologue, '\n') + '\n'
+for line in SCRIPT:
+ program = program + (' \t \t' + line + '\n')
+program = program + (string.joinfields(epilogue, '\n') + '\n')
+
+import tempfile
+fp = tempfile.NamedTemporaryFile()
+fp.write(program)
+fp.flush()
+if DFLAG:
+ import pdb
+ pdb.run('execfile(%r)' % (tfn,))
+else:
+ execfile(tfn)
diff --git a/sys/src/cmd/python/Demo/scripts/primes.py b/sys/src/cmd/python/Demo/scripts/primes.py
new file mode 100755
index 000000000..5935a3c84
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/primes.py
@@ -0,0 +1,27 @@
+#! /usr/bin/env 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
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/queens.py b/sys/src/cmd/python/Demo/scripts/queens.py
new file mode 100755
index 000000000..74756be7d
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/queens.py
@@ -0,0 +1,85 @@
+#! /usr/bin/env python
+
+"""N queens problem.
+
+The (well-known) problem is due to Niklaus Wirth.
+
+This solution is inspired by Dijkstra (Structured Programming). It is
+a classic recursive backtracking approach.
+
+"""
+
+N = 8 # Default; command line overrides
+
+class Queens:
+
+ def __init__(self, n=N):
+ self.n = n
+ self.reset()
+
+ def reset(self):
+ n = self.n
+ self.y = [None]*n # Where is the queen in column x
+ self.row = [0]*n # Is row[y] safe?
+ self.up = [0] * (2*n-1) # Is upward diagonal[x-y] safe?
+ self.down = [0] * (2*n-1) # Is downward diagonal[x+y] safe?
+ self.nfound = 0 # Instrumentation
+
+ def solve(self, x=0): # Recursive solver
+ for y in range(self.n):
+ if self.safe(x, y):
+ self.place(x, y)
+ if x+1 == self.n:
+ self.display()
+ else:
+ self.solve(x+1)
+ self.remove(x, y)
+
+ def safe(self, x, y):
+ return not self.row[y] and not self.up[x-y] and not self.down[x+y]
+
+ def place(self, x, y):
+ self.y[x] = y
+ self.row[y] = 1
+ self.up[x-y] = 1
+ self.down[x+y] = 1
+
+ def remove(self, x, y):
+ self.y[x] = None
+ self.row[y] = 0
+ self.up[x-y] = 0
+ self.down[x+y] = 0
+
+ silent = 0 # If set, count solutions only
+
+ def display(self):
+ self.nfound = self.nfound + 1
+ if self.silent:
+ return
+ print '+-' + '--'*self.n + '+'
+ for y in range(self.n-1, -1, -1):
+ print '|',
+ for x in range(self.n):
+ if self.y[x] == y:
+ print "Q",
+ else:
+ print ".",
+ print '|'
+ print '+-' + '--'*self.n + '+'
+
+def main():
+ import sys
+ silent = 0
+ n = N
+ if sys.argv[1:2] == ['-n']:
+ silent = 1
+ del sys.argv[1]
+ if sys.argv[1:]:
+ n = int(sys.argv[1])
+ q = Queens(n)
+ q.silent = silent
+ q.solve()
+ print "Found", q.nfound, "solutions."
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/script.py b/sys/src/cmd/python/Demo/scripts/script.py
new file mode 100755
index 000000000..6eaa7aec2
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/script.py
@@ -0,0 +1,33 @@
+#! /usr/bin/env python
+# script.py -- Make typescript of terminal session.
+# Usage:
+# -a Append to typescript.
+# -p Use Python as shell.
+# Author: Steen Lumholt.
+
+
+import os, time, sys
+import pty
+
+def read(fd):
+ data = os.read(fd, 1024)
+ file.write(data)
+ return data
+
+shell = 'sh'
+filename = 'typescript'
+mode = 'w'
+if os.environ.has_key('SHELL'):
+ shell = os.environ['SHELL']
+if '-a' in sys.argv:
+ mode = 'a'
+if '-p' in sys.argv:
+ shell = 'python'
+
+file = open(filename, mode)
+
+sys.stdout.write('Script started, file is %s\n' % filename)
+file.write('Script started on %s\n' % time.ctime(time.time()))
+pty.spawn(shell, read)
+file.write('Script done on %s\n' % time.ctime(time.time()))
+sys.stdout.write('Script done, file is %s\n' % filename)
diff --git a/sys/src/cmd/python/Demo/scripts/unbirthday.py b/sys/src/cmd/python/Demo/scripts/unbirthday.py
new file mode 100755
index 000000000..2d0b8e5f4
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/unbirthday.py
@@ -0,0 +1,107 @@
+#! /usr/bin/env python
+
+# Calculate your unbirthday count (see Alice in Wonderland).
+# This is defined as the number of days from your birth until today
+# that weren't your birthday. (The day you were born is not counted).
+# Leap years make it interesting.
+
+import sys
+import time
+import calendar
+
+def main():
+ # Note that the range checks below also check for bad types,
+ # e.g. 3.14 or (). However syntactically invalid replies
+ # will raise an exception.
+ if sys.argv[1:]:
+ year = int(sys.argv[1])
+ else:
+ year = int(raw_input('In which year were you born? '))
+ if 0<=year<100:
+ print "I'll assume that by", year,
+ year = year + 1900
+ print 'you mean', year, 'and not the early Christian era'
+ elif not (1850<=year<=2002):
+ print "It's hard to believe you were born in", year
+ return
+ #
+ if sys.argv[2:]:
+ month = int(sys.argv[2])
+ else:
+ month = int(raw_input('And in which month? (1-12) '))
+ if not (1<=month<=12):
+ print 'There is no month numbered', month
+ return
+ #
+ if sys.argv[3:]:
+ day = int(sys.argv[3])
+ else:
+ day = int(raw_input('And on what day of that month? (1-31) '))
+ if month == 2 and calendar.isleap(year):
+ maxday = 29
+ else:
+ maxday = calendar.mdays[month]
+ if not (1<=day<=maxday):
+ print 'There are no', day, 'days in that month!'
+ return
+ #
+ bdaytuple = (year, month, day)
+ bdaydate = mkdate(bdaytuple)
+ print 'You were born on', format(bdaytuple)
+ #
+ todaytuple = time.localtime()[:3]
+ todaydate = mkdate(todaytuple)
+ print 'Today is', format(todaytuple)
+ #
+ if bdaytuple > todaytuple:
+ print 'You are a time traveler. Go back to the future!'
+ return
+ #
+ if bdaytuple == todaytuple:
+ print 'You were born today. Have a nice life!'
+ return
+ #
+ days = todaydate - bdaydate
+ print 'You have lived', days, 'days'
+ #
+ age = 0
+ for y in range(year, todaytuple[0] + 1):
+ if bdaytuple < (y, month, day) <= todaytuple:
+ age = age + 1
+ #
+ print 'You are', age, 'years old'
+ #
+ if todaytuple[1:] == bdaytuple[1:]:
+ print 'Congratulations! Today is your', nth(age), 'birthday'
+ print 'Yesterday was your',
+ else:
+ print 'Today is your',
+ print nth(days - age), 'unbirthday'
+
+def format((year, month, day)):
+ return '%d %s %d' % (day, calendar.month_name[month], year)
+
+def nth(n):
+ if n == 1: return '1st'
+ if n == 2: return '2nd'
+ if n == 3: return '3rd'
+ return '%dth' % n
+
+def mkdate((year, month, day)):
+ # Januari 1st, in 0 A.D. is arbitrarily defined to be day 1,
+ # even though that day never actually existed and the calendar
+ # was different then...
+ days = year*365 # years, roughly
+ days = days + (year+3)/4 # plus leap years, roughly
+ days = days - (year+99)/100 # minus non-leap years every century
+ days = days + (year+399)/400 # plus leap years every 4 centirues
+ for i in range(1, month):
+ if i == 2 and calendar.isleap(year):
+ days = days + 29
+ else:
+ days = days + calendar.mdays[i]
+ days = days + day
+ return days
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/update.py b/sys/src/cmd/python/Demo/scripts/update.py
new file mode 100755
index 000000000..c9360260e
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/update.py
@@ -0,0 +1,92 @@
+#! /usr/bin/env python
+
+# Update a bunch of files according to a script.
+# The input file contains lines of the form <filename>:<lineno>:<text>,
+# meaning that the given line of the given file is to be replaced
+# by the given text. This is useful for performing global substitutions
+# on grep output:
+
+import os
+import sys
+import re
+
+pat = '^([^: \t\n]+):([1-9][0-9]*):'
+prog = re.compile(pat)
+
+class FileObj:
+ def __init__(self, filename):
+ self.filename = filename
+ self.changed = 0
+ try:
+ self.lines = open(filename, 'r').readlines()
+ except IOError, msg:
+ print '*** Can\'t open "%s":' % filename, msg
+ self.lines = None
+ return
+ print 'diffing', self.filename
+
+ def finish(self):
+ if not self.changed:
+ print 'no changes to', self.filename
+ return
+ try:
+ os.rename(self.filename, self.filename + '~')
+ fp = open(self.filename, 'w')
+ except (os.error, IOError), msg:
+ print '*** Can\'t rewrite "%s":' % self.filename, msg
+ return
+ print 'writing', self.filename
+ for line in self.lines:
+ fp.write(line)
+ fp.close()
+ self.changed = 0
+
+ def process(self, lineno, rest):
+ if self.lines is None:
+ print '(not processed): %s:%s:%s' % (
+ self.filename, lineno, rest),
+ return
+ i = eval(lineno) - 1
+ if not 0 <= i < len(self.lines):
+ print '*** Line number out of range: %s:%s:%s' % (
+ self.filename, lineno, rest),
+ return
+ if self.lines[i] == rest:
+ print '(no change): %s:%s:%s' % (
+ self.filename, lineno, rest),
+ return
+ if not self.changed:
+ self.changed = 1
+ print '%sc%s' % (lineno, lineno)
+ print '<', self.lines[i],
+ print '---'
+ self.lines[i] = rest
+ print '>', self.lines[i],
+
+def main():
+ if sys.argv[1:]:
+ try:
+ fp = open(sys.argv[1], 'r')
+ except IOError, msg:
+ print 'Can\'t open "%s":' % sys.argv[1], msg
+ sys.exit(1)
+ else:
+ fp = sys.stdin
+ curfile = None
+ while 1:
+ line = fp.readline()
+ if not line:
+ if curfile: curfile.finish()
+ break
+ n = prog.match(line)
+ if n < 0:
+ print 'Funny line:', line,
+ continue
+ filename, lineno = prog.group(1, 2)
+ if not curfile or filename <> curfile.filename:
+ if curfile: curfile.finish()
+ curfile = FileObj(filename)
+ curfile.process(lineno, line[n:])
+
+if __name__ == "__main__":
+ main()
diff --git a/sys/src/cmd/python/Demo/scripts/wh.py b/sys/src/cmd/python/Demo/scripts/wh.py
new file mode 100755
index 000000000..b9b09efa6
--- /dev/null
+++ b/sys/src/cmd/python/Demo/scripts/wh.py
@@ -0,0 +1,2 @@
+# This is here so I can use 'wh' instead of 'which' in '~/bin/generic_python'
+import which